Skip to content

Webhook Authentication

Secure your webhooks with authentication.

Authentication Methods

Method Description Use Case
None No authentication Internal/testing
Header Custom header validation Simple API keys
Basic Auth Username/password Standard HTTP auth
HMAC Signature Cryptographic verification Third-party services
JWT Token-based Advanced security

No Authentication

Default - accepts all requests:

{
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "/public-webhook",
    "authentication": "none"
  }
}

Only use for:

  • Internal services
  • Development/testing
  • Public endpoints (with validation)

Header Authentication

Validate a custom header:

{
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "/secure-webhook",
    "authentication": "headerAuth",
    "headerName": "X-API-Key",
    "headerValue": "={{ $env.WEBHOOK_API_KEY }}"
  }
}

Caller must include:

curl -X POST http://localhost:8080/webhook/secure-webhook \
  -H "X-API-Key: your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{"data": "test"}'

Basic Authentication

HTTP Basic auth:

{
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "/basic-auth-webhook",
    "authentication": "basicAuth"
  },
  "credentials": {
    "httpBasicAuth": {
      "id": "cred-123"
    }
  }
}

Caller:

curl -X POST http://localhost:8080/webhook/basic-auth-webhook \
  -u username:password \
  -H "Content-Type: application/json" \
  -d '{"data": "test"}'

HMAC Signature Verification

Verify webhook signatures from services like GitHub, Stripe:

GitHub Webhooks

{
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "/github",
    "authentication": "hmac",
    "hmacSecret": "={{ $env.GITHUB_WEBHOOK_SECRET }}",
    "hmacHeader": "X-Hub-Signature-256",
    "hmacAlgorithm": "sha256"
  }
}

Stripe Webhooks

{
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "/stripe",
    "authentication": "hmac",
    "hmacSecret": "={{ $env.STRIPE_WEBHOOK_SECRET }}",
    "hmacHeader": "Stripe-Signature"
  }
}

Custom HMAC

Generate signature on caller side:

import hmac
import hashlib
import json

body = json.dumps({"event": "test"})
secret = "your-secret"
signature = hmac.new(
    secret.encode(),
    body.encode(),
    hashlib.sha256
).hexdigest()

Include in request:

curl -X POST http://localhost:8080/webhook/my-webhook \
  -H "X-Signature: sha256=abc123..." \
  -H "Content-Type: application/json" \
  -d '{"event": "test"}'

JWT Authentication

Validate JWT tokens:

{
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "/jwt-webhook",
    "authentication": "jwt",
    "jwtSecret": "={{ $env.JWT_SECRET }}",
    "jwtHeader": "Authorization"
  }
}

Caller:

curl -X POST http://localhost:8080/webhook/jwt-webhook \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"data": "test"}'

Access JWT claims:

{{ $json.jwt.sub }}      // Subject
{{ $json.jwt.exp }}      // Expiration
{{ $json.jwt.claims }}   // Custom claims

IP Allowlist

Restrict by source IP:

{
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "/ip-restricted",
    "ipAllowlist": [
      "192.168.1.0/24",
      "10.0.0.1"
    ]
  }
}

Or configure globally:

webhooks:
  ipAllowlist:
    - "192.168.1.0/24"
    - "10.0.0.0/8"

Custom Validation

Add a Filter or Code node after webhook:

Filter Node

{
  "type": "n8n-nodes-base.filter",
  "parameters": {
    "conditions": [
      {
        "leftValue": "={{ $json.headers['x-custom-token'] }}",
        "operator": "equals",
        "rightValue": "expected-value"
      }
    ]
  }
}

Code Node

// Custom validation logic
const token = $json.headers['x-api-key'];
const validTokens = ['token1', 'token2', 'token3'];

if (!validTokens.includes(token)) {
  throw new Error('Invalid API key');
}

return items;

Service-Specific Authentication

GitHub

{
  "parameters": {
    "authentication": "hmac",
    "hmacSecret": "={{ $env.GITHUB_SECRET }}",
    "hmacHeader": "X-Hub-Signature-256",
    "hmacAlgorithm": "sha256"
  }
}

Stripe

{
  "parameters": {
    "authentication": "stripe"
  },
  "credentials": {
    "stripeWebhook": {
      "id": "cred-stripe"
    }
  }
}

Slack

Validate Slack signing secret:

{
  "parameters": {
    "authentication": "hmac",
    "hmacSecret": "={{ $env.SLACK_SIGNING_SECRET }}",
    "hmacHeader": "X-Slack-Signature"
  }
}

Twilio

{
  "parameters": {
    "authentication": "hmac",
    "hmacSecret": "={{ $env.TWILIO_AUTH_TOKEN }}",
    "hmacHeader": "X-Twilio-Signature"
  }
}

Security Best Practices

1. Always Authenticate Production Webhooks

Never expose unauthenticated webhooks in production.

2. Use Strong Secrets

# Generate strong secret
openssl rand -hex 32

3. Store Secrets Securely

Use environment variables or credential manager:

export WEBHOOK_SECRET="generated-secret"

4. Use HTTPS

Always use HTTPS in production:

https://your-domain.com/webhook/secure

5. Validate Timestamps

Prevent replay attacks:

const timestamp = $json.headers['x-timestamp'];
const now = Date.now();
const fiveMinutes = 5 * 60 * 1000;

if (Math.abs(now - timestamp) > fiveMinutes) {
  throw new Error('Request too old');
}

6. Rate Limit

Prevent abuse:

webhooks:
  rateLimit:
    enabled: true
    requests: 100
    window: 60

7. Log and Monitor

Track webhook activity:

console.log({
  path: $json.webhookUrl,
  method: $json.httpMethod,
  ip: $json.headers['x-forwarded-for'],
  timestamp: new Date().toISOString()
});

Error Responses

Status Description
401 Authentication failed
403 IP not allowed
429 Rate limit exceeded

Testing Authentication

Test Header Auth

# Should succeed
curl -X POST http://localhost:8080/webhook/secure \
  -H "X-API-Key: correct-key"

# Should fail (401)
curl -X POST http://localhost:8080/webhook/secure \
  -H "X-API-Key: wrong-key"

Test HMAC

# Generate signature
BODY='{"test": "data"}'
SIGNATURE=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "your-secret" | cut -d' ' -f2)

# Send request
curl -X POST http://localhost:8080/webhook/hmac \
  -H "X-Signature: sha256=$SIGNATURE" \
  -H "Content-Type: application/json" \
  -d "$BODY"