Otomasi3 Agustus 20268 mnt baca

Webhook n8n Tidak Berfungsi: Tujuh Penyebab, Diurutkan dari yang Paling Sering

Sembilan dari sepuluh kali, penyebabnya URL test yang hanya mendengarkan saat dipantau. Sisanya: mode respons, raw body yang di-parse, atau event hilang.

/webhook-test/… · MENUNGGU SATU EVENTAnda mengeklikListen for test1 eksekusiAnda pindah halaman404untuk semuanyaselamanya/webhook/… · HANYA ADA SELAMA DIAKTIFKANworkflowtersimpan, nonaktif404aktifkanroute aktifdisimpan ≠ diaktifkan
Dua URL yang tampak hampir identik, tetapi perilakunya sama sekali berbeda. Di sebagian besar kasus, inilah jawabannya.

Workflow sudah Anda buat, berjalan mulus saat Anda mengeklik Listen for test event (dengarkan event uji), URL-nya sudah Anda tempel di Stripe, dan sekarang tidak terjadi apa-apa. Tidak ada eksekusi. Tidak ada error. Provider melaporkan pengirimannya gagal, atau lebih parah, melaporkan berhasil, padahal Anda tetap tidak menerima apa pun.

Penyebabnya hanya segelintir, tetapi kebingungan yang menyertainya bisa berlarut-larut, terutama karena n8n nyaris tidak bersuara saat gagal dan penyebab yang paling umum adalah URL yang tampak benar. Berikut daftarnya, sesuai urutan seberapa sering masing-masing benar-benar menjadi penyebab.

TL;DR

  • URL test vs URL production adalah jawabannya di sebagian besar kasus. URL test hanya mendengarkan selama Anda memantaunya.
  • URL production baru ada setelah workflow diaktifkan.
  • Secara default, mode respons segera membalas. Itu aman untuk sebagian besar provider, tetapi fatal jika Anda harus mengembalikan challenge.
  • Raw Body wajib aktif untuk verifikasi tanda tangan (signature) apa pun. Jika tidak, HMAC tidak akan pernah cocok.
  • Eksekusi yang gagal membuat event terdampar. n8n tidak mencoba ulang webhook yang masuk.
  • Instance self-hosted di balik reverse proxy menambah lapisan batas ukuran body dan timeout yang sering terlupakan.

1. Anda memakai URL test (biasanya inilah penyebabnya)

Node Webhook di n8n menampilkan dua URL, dan perilaku keduanya sama sekali berbeda:

Test:        https://your-instance/webhook-test/abc-123
Production:  https://your-instance/webhook/abc-123

URL test hanya aktif selama Anda sudah mengeklik Listen for test event dan sedang memantau kanvas. URL ini mencatat satu eksekusi, menampilkan datanya, lalu berhenti. Begitu Anda berpindah halaman, URL ini membalas 404 untuk semua permintaan.

Desain itu tepat sekali untuk tahap membangun, dan sama sekali keliru untuk URL yang Anda tempel di Stripe. Karena semuanya berjalan sempurna selama pengembangan, yang terbentuk di kepala adalah “webhook-nya berfungsi”, sehingga penyebab sebenarnya sulit terlihat.

/webhook-test/… · MENUNGGU SATU EVENTAnda mengeklikListen for test1 eksekusiAnda pindah halaman404untuk semuanyaselamanya/webhook/… · HANYA ADA SELAMA DIAKTIFKANworkflowtersimpan, nonaktif404aktifkanroute aktifdisimpan ≠ diaktifkan

Periksa URL yang terdaftar di provider. Jika mengandung webhook-test, itulah bug-nya, dan Anda bisa berhenti membaca di sini.

2. Workflow belum diaktifkan

URL production didaftarkan saat workflow diaktifkan (dipublikasikan). URL production milik workflow yang tidak aktif membalas 404, karena bagi server, route itu memang tidak ada.

Jebakannya, menyimpan tidak sama dengan mengaktifkan. Anda bisa mengedit, menyimpan, dan menutup workflow seharian, dan route production tetap tidak pernah diperbarui. Kegagalan lain dari keluarga yang sama:

  • Menonaktifkan workflow untuk mengubah sesuatu, lalu lupa mengaktifkannya kembali. Setiap event selama rentang waktu itu hilang.
  • Menduplikasi workflow untuk menguji perubahan. Salinannya tidak aktif, dan yang menerima trafik tetap workflow aslinya.
  • Mengubah path node webhook, yang ikut mengubah URL-nya. Provider masih mengarah ke URL lama.

Periksa toggle di editor, dan pastikan URL di provider sama persis dengan path yang sekarang.

3. Mode respons tidak sesuai dengan yang diharapkan provider

Pengaturan Respond di node Webhook, yaitu mode responsnya, menentukan kapan dan apa yang dibalas n8n:

ModePerilaku
Immediately (segera)Mengembalikan 200 dengan pesan “Workflow was started” begitu event tiba
When Last Node Finishes (saat node terakhir selesai)Membiarkan koneksi tetap terbuka sampai workflow selesai, lalu mengembalikan output node terakhir
Using 'Respond to Webhook' nodeAnda mengatur status dan body secara eksplisit

Immediately adalah default yang tepat, dan sebaiknya tetap Anda pakai. Mode ini membalas dengan cepat, sehingga balasan Anda tetap masuk dalam batas waktu respons setiap provider, berapa pun lamanya workflow berjalan.

Ada dua kasus ketika mode ini keliru.

Handshake verifikasi. Beberapa provider mengharuskan Anda mengembalikan nilai challenge saat mendaftarkan langganan, misalnya token atau hash dari body. Immediately membalas dengan pesan tetap, dan langganannya ditolak. Anda perlu node Respond to Webhook untuk mengembalikan nilai persis yang diharapkan.

When Last Node Finishes dengan workflow yang lambat. Kini waktu respons Anda sama dengan durasi seluruh workflow. Tiga panggilan API dan satu penulisan ke Google Sheets dengan mudah memakan 8 detik, sementara batas waktu respons Shopify lima detik. Provider mencatat timeout, mencoba ulang, dan workflow Anda berjalan untuk kedua kalinya dari awal. Inilah mode yang diam-diam membuat baris duplikat.

Jika Anda memakai When Last Node Finishes dan tidak benar-benar membutuhkan output di dalam respons, ganti ke Immediately.

4. Verifikasi tanda tangan gagal karena Raw Body nonaktif

Jika Anda memverifikasi tanda tangan Stripe, Shopify, atau GitHub di dalam n8n, opsi Raw Body di node Webhook wajib diaktifkan.

Provider menghitung HMAC dari byte persis yang mereka kirim. Saat Raw Body nonaktif, n8n mem-parse JSON-nya, dan node kode apa pun yang menyerialisasinya ulang akan menghasilkan byte yang berbeda: urutan key berbeda, whitespace berbeda. Datanya identik, tetapi tanda tangannya tidak. Anda akan menatap payload yang jelas-jelas benar sementara verifikasinya terus gagal, dan itu satu jam yang benar-benar menjengkelkan.

Aktifkan Raw Body, lalu hitung HMAC dari string mentahnya sebelum mem-parse apa pun:

const crypto = require('crypto');
const raw = $input.first().binary
  ? Buffer.from($input.first().binary.data.data, 'base64').toString('utf8')
  : $input.first().json.body;

const expected = crypto
  .createHmac('sha256', $env.SHOPIFY_SECRET)
  .update(raw, 'utf8')
  .digest('base64');

const received = $input.first().json.headers['x-shopify-hmac-sha256'];
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))) {
  throw new Error('invalid signature');
}
return { json: JSON.parse(raw) };

Gunakan timingSafeEqual, bukan ===. Perhatikan juga bahwa fungsi ini melempar error jika panjangnya tidak sama, jadi beri pengaman jika inputnya tidak tepercaya. Format tiap provider dibahas lebih lanjut di Verifikasi Tanda Tangan Webhook Stripe, GitHub, dan Shopify.

5. Eksekusi yang gagal membuat event terdampar

Penyebab inilah yang menimbulkan kerugian nyata, dan ini bukan bug. Memang begitulah cara kerja modelnya.

Dengan Respond diatur ke Immediately, n8n sudah mengembalikan 200 sebelum workflow Anda berjalan. Provider menganggap event sudah terkirim dan tidak akan pernah mengirimnya lagi. Jika node 4 kemudian gagal karena token kedaluwarsa, rate limit, atau field yang formatnya salah, eksekusi itu ditandai gagal, dan event-nya tidak ada di mana pun selain di catatan eksekusi yang gagal itu.

Tidak ada percobaan ulang (retry) otomatis untuk webhook yang masuk. Tidak ada antrean di depannya. Provider tidak akan mengirim ulang, karena dari sudut pandangnya semuanya berjalan lancar.

Yang bisa Anda lakukan:

  • Atur Error Workflow di pengaturan workflow. Minimal, workflow ini harus memberi tahu Anda. Gagal tanpa suara adalah default-nya, dan itu bukan default yang baik.
  • Simpan payload mentah di node 1. Sebelum transformasi apa pun, tulis body ke Postgres, Airtable, Google Sheet, apa saja yang persisten. Dengan begitu, eksekusi yang gagal dapat diputar ulang (replay) dari penyimpanan Anda sendiri, bukan hilang.
  • Gunakan continueOnFail dengan sengaja pada node yang boleh berhasil sebagian, dan jangan pada node tempat opsi itu diam-diam menelan kegagalan yang seharusnya Anda lihat.
  • Periksa masa simpan. n8n self-hosted menghapus data eksekusi secara terjadwal, dan setelah terhapus, payload dari eksekusi yang gagal pun ikut hilang.

6. Self-hosted: reverse proxy menghalangi

Jika Anda menjalankannya secara self-hosted di balik nginx, Traefik, atau Caddy, ada beberapa lapisan yang bisa menelan permintaan sebelum n8n melihatnya:

  • Batas ukuran body. Secara default, nginx mengatur client_max_body_size ke 1MB dan membalas 413 untuk apa pun yang lebih besar. Payload massal Shopify atau lampiran base64 akan melampauinya.
  • Timeout proxy. Jika proxy_read_timeout lebih pendek dari durasi workflow, proxy menutup koneksi dan provider mencatat kegagalan, padahal n8n berhasil menyelesaikannya.
  • WEBHOOK_URL tidak diatur. n8n membuat URL yang ditampilkannya dari variabel ini. Di balik proxy, yang ditampilkan adalah alamat internal yang bisa dibuka dari laptop Anda, tetapi tidak dari internet.
  • Worker di queue mode. Dalam queue mode, webhook harus sampai ke instance utama, dan mengarahkannya ke worker akan berujung 404.

Diagnosis cepatnya: jalankan curl -X POST ke URL production Anda dari luar jaringan. Respons 404 berarti masalah routing atau aktivasi. Respons 413 berarti ukuran body. Permintaan yang menggantung berarti timeout. Respons 200 tanpa apa pun di tab Executions berarti eksekusinya tidak disimpan: periksa pengaturan workflow untuk menyimpan eksekusi production.

7. Event-nya sampai, tetapi filter membuangnya

Lebih jarang, tetapi perlu dipastikan bukan ini penyebabnya: eksekusinya ada, tetapi tampaknya tidak melakukan apa-apa. Cocokkan kondisi di node IF atau Switch dengan bentuk payload yang sebenarnya, karena struktur bersarang payload dari provider berbeda dengan payload uji yang menjadi acuan saat Anda membangun workflow. Jika tidak ada eksekusi sama sekali, periksa HTTP Method di node, yang default-nya GET, karena provider yang mengirim POST ke webhook khusus GET akan mendapat 404 yang terlihat sama persis dengan masalah routing.

Peran AnyHook

Penyebab 5 adalah masalah struktural. n8n menjalankan workflow. Event tidak disimpan lebih dulu, dan begitu 200 dikembalikan, satu-satunya salinan lain yang pernah ada adalah milik provider.

Menempatkan AnyHook di depannya mengubah arti “terkirim”. Anda mengarahkan provider ke in.anyhook.net/you/app dan mengarahkan AnyHook ke URL production n8n Anda: cukup dua perubahan URL, tanpa menulis ulang workflow. Setelah itu:

  • Event disimpan secara persisten sebelum n8n dipanggil, jadi eksekusi yang gagal dapat diputar ulang dari log.
  • Percobaan ulang berjalan dengan backoff eksponensial ke instance n8n Anda, jadi saat instance restart karena pembaruan versi, pengiriman pulih dengan sendirinya.
  • Verifikasi tanda tangan dilakukan di edge (Cloudflare Worker AnyHook di in.anyhook.net). Atur signing secret dari provider sekali, dan Anda mendapat satu header AnyHook-Signature, sehingga kerepotan Raw Body di penyebab 4 menjadi satu pemeriksaan HMAC yang Anda tulis sekali untuk semua provider.
  • Peringatan kegagalan dikirim pada 1, 5, dan 20 kegagalan berturut-turut, dengan jeda otomatis agar workflow yang rusak tidak dibanjiri percobaan ulang.

Yang paling terasa dalam praktik: pembaruan n8n yang membuat instance mati selama sepuluh menit bisa membuat Anda kehilangan setiap event dalam sepuluh menit itu. Dengan relay di depannya, ongkosnya hanya satu klik putar ulang.

Penyiapannya tanpa kode:

  1. Buat App di AnyHook (satu URL masuk untuk satu sumber webhook). Pilih pengirim sebagai sumbernya jika tercantum (Shopify tercantum), atau Generic (umum) untuk pengirim yang tidak tercantum, misalnya Typeform. Atur tujuannya ke URL webhook production milik workflow Anda.
  2. Ganti URL webhook di pengirim dengan URL masuk (inbound URL) milik App. Di admin Shopify, letaknya di Settings → Notifications → Webhooks; di Typeform, Connect → Webhooks.
  3. Ganti node Shopify Trigger atau Typeform Trigger bawaan n8n dengan node Webhook biasa jika workflow Anda diawali salah satunya. Node trigger mendaftarkan webhook-nya sendiri ke provider, sehingga AnyHook terlewati.
  4. Buka App di AnyHook setelah gangguan, lalu putar ulang dari banner event yang gagal.

Satu App untuk satu pengirim, dan paket Free mencakup satu App, jadi pengirim kedua berarti paket Pro.

Kesimpulan

Periksa URL lebih dulu. webhook-test versus webhook jauh lebih sering menjadi jawabannya dibanding penyebab lain, dan aktivasi menyusul tidak jauh di belakang. Setelah webhook mulai menerima data, yang perlu dibereskan sebelum terlupa adalah persistensi: tulis payload mentah di node pertama, dan atur Error Workflow. n8n tidak akan mengirim ulang event yang sudah dibuangnya, dan provider juga tidak.

Semua artikel3 Agustus 2026 · 8 mnt

Berhenti kehilangan webhook.

Ganti satu URL. Dapatkan percobaan ulang, log event, dan putar ulang sekali klik. Atau lihat dulu: daftar dan jalankan latihan gangguan 30 detik, lalu saksikan event tetap selamat saat server Anda mati. Tidak perlu server.