Skip to main content
教程11 min read

n8n Webhook 不工作?修复 Nginx、Traefik 和 Caddy 背后的 404 错误

修复自托管部署中 n8n webhook 404 错误的完整指南。涵盖 WEBHOOK_URL、N8N_PROXY_HOPS 和 Nginx 配置。

作者 AutomationVPS

The Most Common Self-Hosted n8n Problem

You set up n8n on your VPS, build a workflow with a Webhook trigger, test it in the editor -- and it works perfectly. Then you activate the workflow, send a request from an external service, and get:

{"code": 404, "message": "The requested webhook is not registered."}

This is the single most reported issue in the n8n community forum. It affects nearly every self-hosted deployment behind a reverse proxy, and the root cause is almost always a misconfigured environment variable or a misunderstanding of how n8n registers webhooks.

Here's how to fix it permanently.

The Two Environment Variables That Fix 90% of Webhook Issues

WEBHOOK_URL

This is the strongest override. It tells n8n exactly what base URL to use for all webhook registrations. If you're behind a reverse proxy, this should be your public domain:

WEBHOOK_URL=https://n8n.yourdomain.com/

Without this, n8n constructs webhook URLs from its internal view of the network -- which behind a proxy is typically http://localhost:5678. External services can't reach that.

N8N_PROXY_HOPS

This tells n8n how many reverse proxies sit between it and the internet. For most setups (one Nginx, Traefik, or Caddy instance), set it to 1:

N8N_PROXY_HOPS=1

When this is set, n8n reads the X-Forwarded-* headers from your proxy to determine the real protocol, host, and client IP. Without it, n8n ignores these headers entirely.

If your webhook URLs show http:// instead of https://, or show port 5678 in the URL, you're missing one or both of these variables.

Complete Docker Compose Environment

Here's a production-ready environment configuration that prevents webhook issues:

services:
  n8n:
    image: n8nio/n8n:latest
    restart: always
    ports:
      - "5678:5678"
    environment:
      - N8N_HOST=n8n.yourdomain.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.yourdomain.com/
      - N8N_PROXY_HOPS=1
      - GENERIC_TIMEZONE=UTC
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=your-secure-password
    volumes:
      - n8n_data:/home/node/.n8n

Note the key settings: N8N_PROTOCOL=https, WEBHOOK_URL with the full public domain including trailing slash, and N8N_PROXY_HOPS=1.

Test Webhooks vs Production Webhooks

This catches a lot of people. n8n has two webhook paths that behave completely differently:

FeatureTest WebhookProduction Webhook
URL path/webhook-test/{id}/webhook/{id}
When activeOnly while "Listen for Test Event" is openOnly when workflow is Active
LifespanSingle call, or 120-second timeoutPermanent (until workflow deactivated)
Shows data in editorYesNo
Requires active workflowNoYes

The Trap

You build a workflow, click "Listen for Test Event", send a request to /webhook-test/..., and it works. You then give the same test URL to an external service. It works once (because the test listener was still active), then stops. Or you use the production URL (/webhook/...) but forget to activate the workflow.

The fix: Always use the production URL (/webhook/...) for external integrations, and always toggle the workflow to Active before expecting it to receive data.

Nginx Configuration for n8n

This is the proven Nginx server block for n8n behind a reverse proxy:

server {
    listen 80;
    server_name n8n.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name n8n.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:5678;

        # Required: Forward client identity
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;

        # Required: WebSocket support for editor
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Required: Allow large webhook payloads
        client_max_body_size 50m;

        # Recommended: Increase timeouts for long workflows
        proxy_connect_timeout 300s;
        proxy_send_timeout 300s;
        proxy_read_timeout 300s;

        # Recommended: Disable buffering for real-time events
        proxy_buffering off;
        proxy_cache off;
    }
}

Why Each Header Matters

  • X-Forwarded-Proto -- Without this, n8n doesn't know the connection is HTTPS. Webhook URLs will show http:// even though your site uses SSL.
  • X-Forwarded-Host -- Tells n8n the real hostname. Without it, webhook URLs may contain localhost or the container's internal hostname.
  • X-Forwarded-For -- Passes the client's real IP. Important for rate limiting and logging.
  • Upgrade + Connection -- Enables WebSocket connections. Without these, the n8n editor's real-time features (including test webhook listening) break silently.
  • client_max_body_size -- Default Nginx limit is 1 MB. Webhook payloads with file attachments need more.
⚠️

If WebSocket headers are missing, the n8n editor will appear to work but "Listen for Test Event" will never receive data. You'll think webhooks are broken when it's actually the WebSocket connection that's failing.

Traefik Configuration

If you're using Traefik instead of Nginx, add these labels to your n8n Docker Compose service:

services:
  n8n:
    image: n8nio/n8n:latest
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.n8n.rule=Host(`n8n.yourdomain.com`)"
      - "traefik.http.routers.n8n.entrypoints=websecure"
      - "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
      - "traefik.http.services.n8n.loadbalancer.server.port=5678"

Traefik handles X-Forwarded headers automatically, but you still need WEBHOOK_URL and N8N_PROXY_HOPS in your n8n environment.

Caddy Configuration

Caddy is the simplest option for n8n reverse proxying. It handles SSL, WebSocket, and forwarded headers automatically:

n8n.yourdomain.com {
    reverse_proxy localhost:5678
}

That's it. Caddy auto-provisions Let's Encrypt certificates, sets all required X-Forwarded headers, and handles WebSocket upgrades by default. You still need WEBHOOK_URL and N8N_PROXY_HOPS in your n8n environment.

Hostinger

Hostinger VPS comes with full root access for Nginx, Traefik, or Caddy setup. KVM 1 at $6.49/mo handles n8n + reverse proxy with room to spare.

Visit Hostinger

* Affiliate link — we may earn a commission at no extra cost to you.

n8n v2.0 Webhook Changes

If you recently upgraded to n8n v2.0, be aware of these breaking changes:

OAuth callback authentication changed. The default for N8N_SKIP_AUTH_ON_OAUTH_CALLBACK changed from true to false. If your OAuth integrations stopped working after upgrading, set this variable explicitly:

N8N_SKIP_AUTH_ON_OAUTH_CALLBACK=true

Respond to Webhook node behavior changed. In v2.0, the Respond to Webhook node may return an empty body even when the node executes successfully. Test your webhook response workflows after upgrading.

Environment variable parsing changed. Backtick characters in .env values must be wrapped in quotes in v2.0.

Step-by-Step Troubleshooting Checklist

When your webhook returns 404, work through this list in order:

1. Is the workflow active? Open the n8n editor, go to the workflow, and check the toggle in the top-right. It must show "Active". Just saving a workflow doesn't activate it.

2. Are you using the production URL? Production webhooks use /webhook/{path}. Test webhooks use /webhook-test/{path}. External services should always use the production path.

3. Is WEBHOOK_URL set correctly? Check your Docker environment. The URL should include https://, your domain, and a trailing slash:

docker exec n8n env | grep WEBHOOK

4. Is N8N_PROXY_HOPS set?

docker exec n8n env | grep PROXY_HOPS

Should return 1 (or more if you have multiple proxies).

5. Is your reverse proxy forwarding headers? Test with curl from your server:

curl -I https://n8n.yourdomain.com/webhook/test-path

If this returns a connection error, your reverse proxy or SSL isn't configured correctly.

6. Did you recently restart n8n? After a restart, webhook registrations happen via the Task Broker when workflows are saved in the UI. If webhooks stop after a restart, open each affected workflow in the editor and click Save again.

7. Are there duplicate webhook paths? Two workflows using the same webhook path and HTTP method will conflict. Check for duplicates.

8. Check n8n logs for errors:

docker logs n8n --tail 100 | grep -i webhook

9. Test the webhook directly:

curl -X POST https://n8n.yourdomain.com/webhook/your-webhook-path \
  -H "Content-Type: application/json" \
  -d '{"test": true}'

A 200 response means the webhook works. A 404 means it's not registered. A connection error means the proxy or network is the problem.

💡

For debugging, temporarily set N8N_LOG_LEVEL=debug in your environment. This will log every webhook registration and incoming request, making it easy to spot mismatches.

Preventing Webhook Issues Long-Term

Once your webhooks work, keep them working:

  • Pin your n8n version in Docker Compose (image: n8nio/n8n:1.82.1 instead of :latest) to avoid surprise breaking changes
  • Set up monitoring -- use UptimeRobot or a similar service to ping your webhook URL every 5 minutes
  • Automate SSL renewal -- expired certificates cause silent webhook failures
  • Back up your environment variables -- losing N8N_ENCRYPTION_KEY after a migration will break all stored credentials and webhook configurations

Conclusion

n8n webhook 404 errors are frustrating but predictable. In almost every case, the fix is some combination of setting WEBHOOK_URL, adding N8N_PROXY_HOPS=1, and ensuring your reverse proxy forwards the right headers.

Get the environment variables right from the start, use the Nginx/Traefik/Caddy configs above, and you'll have bulletproof webhook endpoints that external services can rely on.

DigitalOcean

DigitalOcean's managed load balancers handle SSL and proxy headers automatically. Pair with an n8n Droplet for the easiest webhook setup.

Visit DigitalOcean

* Affiliate link — we may earn a commission at no extra cost to you.

准备好开始自动化了吗?立即获取VPS。

立即开始使用 Hostinger VPS 主机。特惠价格可用。

获取 Hostinger VPS

* 联盟链接 — 我们可能会获得佣金,不会增加您的费用

#n8n#webhooks#nginx#reverse-proxy#troubleshooting