Webhook w n8n: jak działa, adres testowy vs produkcyjny, bezpieczeństwo
Webhook to węzeł-trigger, który daje Twojemu workflow publiczny adres URL. Gdy ktoś wyśle pod niego żądanie HTTP (formularz, sklep, CRM, inny system), workflow rusza z danymi z tego żądania. Dziewięć na dziesięć „webhook nie działa" to różnica między adresem testowym a produkcyjnym.
Dwa adresy jednego webhooka
| Test URL | Production URL | |
|---|---|---|
| Ścieżka | /webhook-test/<path> | /webhook/<path> |
| Kiedy nasłuchuje | tylko po kliknięciu „Test workflow", na jedno żądanie | zawsze, gdy workflow jest Active |
| Gdzie widać dane | w edytorze, na żywo | w zakładce Executions |
Typowy błąd: adres testowy wklejony do sklepu. Pierwsze żądanie przechodzi (bo klikałeś test), kolejne dostają 404. Do systemów zewnętrznych zawsze wklejaj Production URL i włącz workflow.
Konfiguracja węzła
- HTTP Method: POST dla danych z formularzy i systemów, GET dla prostych wywołań z linku.
- Path: własna, trudna do zgadnięcia ścieżka (np.
zamowienia-7f3k) zamiast domyślnego UUID, jeśli adres ma być czytelny. - Respond: Immediately (200 od razu, workflow leci dalej), When last node finishes (odpowiedź z wyniku ostatniego węzła) albo Using Respond to Webhook node, gdy chcesz zwrócić własny JSON lub kod błędu.
- Dane: ciało żądania trafia do
$json.body, nagłówki do$json.headers, parametry URL do$json.query.
Zabezpieczenie webhooka
- Header Auth (w węźle: Authentication → Header Auth): nadawca musi wysłać nagłówek, np.
X-Api-Key, z sekretem. Najprostsze i wystarczające dla własnych systemów. - Basic Auth: login i hasło w nagłówku Authorization; wspierane przez większość SaaS-ów.
- Podpis HMAC (Stripe, Shopify, GitHub): w węźle Code licz HMAC z surowego ciała kluczem i porównuj z nagłówkiem dostawcy. Wymaga włączenia opcji Raw Body w węźle Webhook.
- Lista IP (opcja IP Whitelist) dla systemów o stałych adresach.
// Weryfikacja podpisu HMAC-SHA256 w węźle Code (Raw Body włączone w Webhooku)
const crypto = require('crypto');
const secret = 'TWOJ_SEKRET';
const sig = $input.first().json.headers['x-signature'];
const raw = $input.first().binary.data ? Buffer.from($input.first().binary.data.data, 'base64') : Buffer.from(JSON.stringify($input.first().json.body));
const expected = crypto.createHmac('sha256', secret).update(raw).digest('hex');
if (sig !== expected) throw new Error('Zły podpis');
return $input.all();
Webhook za reverse proxy (self-host)
n8n buduje adresy webhooków ze zmiennej WEBHOOK_URL. Jeśli instancja stoi za Nginx/Caddy pod https://n8n.firma.pl, a zmiennej nie ma, n8n pokaże http://localhost:5678/webhook/... i nikt z zewnątrz go nie wywoła. Ustaw WEBHOOK_URL=https://n8n.firma.pl/ (ze slashem) i zrestartuj. Po zmianie domeny zaktualizuj adresy we wszystkich systemach, które wysyłają dane. Konfiguracja: n8n w Dockerze.
Testowanie bez formularza
curl -X POST https://n8n.firma.pl/webhook-test/formularz-kontakt \
-H "Content-Type: application/json" \
-H "X-Api-Key: TWOJ_SEKRET" \
-d '{"imie":"Jan","email":"jan@firma.pl","wiadomosc":"Test"}'
Gotowy przepływ do pobrania: Formularz WWW → e-mail (webhook + honeypot + SMTP).
Najczęstsze pytania
Czy webhook działa w n8n Cloud bez konfiguracji?
Tak, Cloud ma publiczny adres od razu. Konfiguracja WEBHOOK_URL dotyczy tylko self-hostu.
Dlaczego webhook zwraca 404 „not registered"?
Workflow nie jest aktywny (adres produkcyjny) albo nie kliknąłeś Test workflow (adres testowy). Sprawdź też, czy path w URL zgadza się co do znaku.
Jak odebrać plik przez webhook?
Wyślij go jako multipart/form-data; n8n umieści plik w polu binarnym elementu, które przetwarzasz węzłami do plików (Read/Write, Extract from File).
Źródła
- n8n docs: Webhook node (dostęp 2026-10-13)
- n8n docs: Respond to Webhook (dostęp 2026-10-13)