IAnswerable

Secure Your n8n Webhooks

03 Dec 2025

An n8n webhook URL is just a public HTTP endpoint. If you can find it, anyone can find it — there’s nothing inherently private about a URL, and n8n’s webhook paths aren’t exactly hard to guess once someone knows the pattern. If that endpoint triggers a workflow that writes to a database, sends money, or calls another API on your behalf, an unauthenticated webhook is an open door.

1. Turn on webhook authentication

n8n’s Webhook node has a built-in Authentication option most people leave on “None.” Set it to Header Auth at minimum:

In the Webhook node, set Authentication → Header Auth, then create a credential with a header name and a secret value, something like:

Header Name: X-Webhook-Secret
Header Value: a long random string

Whoever calls this webhook now has to include that header, or n8n rejects the request before it ever reaches your workflow logic:

curl -X POST https://n8n.example.com/webhook/abc123 \
  -H "X-Webhook-Secret: a long random string" \
  -H "Content-Type: application/json" \
  -d '{"event":"order.created"}'

Generate the secret properly, not “password123”:

openssl rand -hex 32

2. Verify signatures for third-party webhooks, don’t just trust a shared secret

If you’re receiving webhooks from a service that signs its payloads — Stripe, GitHub, most mature APIs — use their signature verification instead of (or in addition to) header auth. A shared secret in a header proves the request knew the secret; a signature proves the exact payload wasn’t tampered with in transit. These aren’t the same guarantee, and for financial or account-sensitive events you want the signature check.

This isn’t a built-in n8n node for every provider, so you typically verify it yourself with a Code node early in the workflow, before anything else runs:

const crypto = require('crypto');
const signature = $input.first().headers['x-signature'];
const expected = crypto
  .createHmac('sha256', $env.WEBHOOK_SECRET)
  .update(JSON.stringify($input.first().body))
  .digest('hex');

if (signature !== expected) {
  throw new Error('Invalid signature');
}

If this check fails, the workflow should stop immediately — don’t let it fall through to the business logic with a logged warning and continue anyway.

3. IP allowlisting for known senders

If the webhook only ever needs to accept traffic from a specific service with published IP ranges (many SaaS webhook senders document theirs), enforce it at the reverse proxy in front of n8n, not inside the workflow — rejecting bad traffic before it reaches n8n at all is cheaper and simpler than filtering it in workflow logic:

location /webhook/abc123 {
    allow 192.0.2.0/24;
    deny all;
    proxy_pass http://n8n_backend;
}

This doesn’t work for webhooks from arbitrary sources (a customer’s own system calling into yours, for example) where you can’t predict the sending IP — use header auth or signature verification there instead.

4. Don’t put secrets in the URL

Some setups put a token directly in the webhook path (/webhook/abc123?token=secret) instead of a header. Avoid this. URLs get logged — by your reverse proxy, by browser history if anyone ever pastes the URL to test it, by any tool that logs full request lines. A header is not immune to logging either, but it’s not the default place things end up, and pairing it with proper header-based auth keeps the secret out of the URL path where server access logs routinely capture it in plaintext.

5. Rate limit at the proxy

A public webhook with no rate limiting is an invitation for someone to hammer it, either maliciously or by a misbehaving retry loop on the sending side. Add a basic limit in front of n8n:

limit_req_zone $binary_remote_addr zone=webhook:10m rate=10r/s;

location /webhook/ {
    limit_req zone=webhook burst=20 nodelay;
    proxy_pass http://n8n_backend;
}

This protects your n8n instance from being overwhelmed and, if you’re on a metered API downstream (an LLM call, a paid third-party API triggered per webhook), protects your bill too.

The baseline for anything that touches money or PII

Header auth, signature verification where the sender supports it, and rate limiting at the proxy. Skipping any one of these on a webhook that triggers a financial or customer-data action isn’t a shortcut — it’s a workflow anyone on the internet can trigger for you.