n8n 502 Bad Gateway Behind a Proxy: Causes and Fixes
n8n showing 502 Bad Gateway, or webhooks not working behind Nginx, Traefik, Caddy or Cloudflare? Find the failing hop and fix it, with configs.
A hotel guest walks up to the front desk and asks for room 512.
The receptionist calls the room. No answer. Calls again. Nothing.
"I'm sorry," she says. "I can't reach him."
That's a 502 Bad Gateway.
The receptionist is your reverse proxy: Nginx, Traefik or Caddy. She's working fine. She's the one telling you there's a problem. The guest who won't pick up is n8n.
So the fix is never at the front desk. It's finding out why the room isn't answering.
Short on time? Run
docker psandcurl -sS http://127.0.0.1:5678/healthzon the server. If n8n isn't running or doesn't answer, the problem is n8n (logs, memory, database). If it answers there but not through your domain, the problem is the proxy config. If webhooks showlocalhost:5678, setWEBHOOK_URL.
Browser or webhook caller
Sees 502 Bad Gateway, or a webhook that never fires
Cloudflare (if proxied)
Its own 502 or 521 page: it can't reach your server at all
Reverse proxy: Nginx, Traefik, Caddy
Proxy is up, n8n isn't answering: wrong host, port or network
n8n on port 5678
Crashed, restarting, or out of memory mid-request
Postgres
n8n can't start without its database, so the proxy gets nothing
moarkid.com — Self-Hosting in Production
Step 1: Is n8n actually running?
docker ps -a --filter name=n8nIf the status says Restarting or Exited, the proxy is telling the truth: nobody's home. Look at why:
docker logs --tail 100 n8n
docker inspect -f 'OOMKilled={{.State.OOMKilled}} Restarts={{.RestartCount}}' n8nThe usual answers:
OOMKilled=true: n8n ran out of memory, often on a workflow that loads a big file or a large dataset into memory. Give the container more RAM, or process the data in batches.- Database errors on startup: n8n can't reach Postgres (wrong password, Postgres not started yet, disk full). n8n won't serve anything until its database is up. If the disk is full, see Docker no space left on device.
- Encryption key mismatch: the container was recreated with a different
N8N_ENCRYPTION_KEYthan the one that encrypted the stored credentials. Use the original key.
Step 2: Does n8n answer locally?
From the server itself, skipping the proxy:
curl -sS http://127.0.0.1:5678/healthz{"status":"ok"} means n8n is fine and the problem is between the proxy and n8n. If you use Postgres, /healthz/readiness also checks the database connection.
No answer here, but the container is running? Check the port mapping in docker-compose.yml. n8n listens on 5678 inside the container, and it has to be published or reachable on the network your proxy uses.
Step 3: Read the proxy's error log
The proxy writes down exactly why it gave up. For Nginx:
sudo tail -n 50 /var/log/nginx/error.log| Message in the log | What it means |
|---|---|
connect() failed (111: Connection refused) | Nothing listening at that host and port |
upstream prematurely closed connection | n8n accepted, then died mid-request (check OOM) |
no live upstreams | Every upstream failed its checks |
upstream timed out (110: Connection timed out) | n8n too slow to answer, usually a 504 instead |
The Docker networking trap
The most common cause of Connection refused: Nginx runs in its own container and the config says proxy_pass http://127.0.0.1:5678.
Inside a container, 127.0.0.1 means that container, not the server. Nginx is knocking on its own door. Put both containers on the same Docker network and use the service name:
proxy_pass http://n8n:5678;If Nginx runs directly on the server (not in Docker), 127.0.0.1:5678 is right, as long as the n8n container publishes the port (127.0.0.1:5678:5678 keeps it private to the server).
A working Nginx config for n8n
/etc/nginx/sites-available/n8n
server {
listen 443 ssl;
server_name n8n.example.com;
# ssl_certificate lines from certbot go here
location / {
proxy_pass http://127.0.0.1:5678;
# The editor uses a WebSocket; without these it shows "Connection lost"
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 300s;
}
}On Caddy, one line does the same job, WebSockets included:
Caddyfile
n8n.example.com {
reverse_proxy n8n:5678
}On Traefik, route the n8n service to port 5678 with a traefik.http.services.n8n.loadbalancer.server.port=5678 label. Traefik handles WebSockets without extra config.
The n8n side: four environment variables
Even with a perfect proxy, n8n needs to know it lives behind one:
.env
N8N_HOST=n8n.example.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.example.com/
N8N_PROXY_HOPS=1WEBHOOK_URLmakes n8n show your public address in webhook nodes. Without it, n8n builds URLs from its internal address, so the webhook URL you paste into Stripe or your form tool points tolocalhost:5678, which no one on the internet can reach.N8N_PROXY_HOPS=1tells n8n to trust one proxy'sX-Forwarded-Forheader. Without it, the logs can fill withERR_ERL_UNEXPECTED_X_FORWARDED_FORerrors. Use2if a cloud load balancer also sits in front of the proxy.
Restart n8n after changing them (docker compose up -d).
Webhooks still not firing?
Three things that aren't a proxy problem at all:
- Test URL vs production URL. The
/webhook-test/URL only works while you are clicking "Listen for test event" in the editor. Outside services need the/webhook/production URL. - The workflow must be published. In n8n 2.x the old "Active" toggle became Publish, and a production webhook only exists for a published workflow. Edits are saved as drafts: until you publish again, the webhook keeps running the last published version, which is why "I fixed it and nothing changed" is so common.
- Cloudflare error 524. If your domain is proxied through Cloudflare, it gives up after 100 seconds, and on Free, Pro and Business plans that can't be raised. For slow workflows, add a Respond to Webhook node early so the caller gets an answer right away, and let the slow part run after it.
The bottom line
A 502 is the front desk saying the room isn't answering. Don't argue with the receptionist.
Check the room first (docker ps, healthz), then the phone line between them (the proxy config and Docker network), then the address on the door (WEBHOOK_URL). The first one that fails is your cause.
P.S. Send me the last 20 lines of docker logs n8n and your proxy's error log (remove any secrets). I'll reply with what I think is wrong, in one message. Free.
Frequently Asked Questions
- Why does n8n return 502 Bad Gateway?
- A 502 comes from the reverse proxy in front of n8n, not from n8n itself. It means the proxy could not get a valid response from n8n: the container is down or restarting, the proxy points at the wrong host or port, or n8n crashed mid-request, often from running out of memory.
- Why are my n8n webhooks not working behind a reverse proxy?
- Usually because WEBHOOK_URL is not set, so n8n generates webhook URLs with localhost:5678 that no outside service can reach. Set WEBHOOK_URL to your public HTTPS address and restart n8n. Also check that you are using the production URL and that the workflow is published (active in n8n 1.x); in n8n 2.x, edits only go live after you publish again.
- What is N8N_PROXY_HOPS?
- It tells n8n how many reverse proxies sit in front of it, so it trusts the right X-Forwarded-For entry for the real client IP. Set it to 1 for a single Nginx, Traefik or Caddy in front of n8n. Without it you may see ERR_ERL_UNEXPECTED_X_FORWARDED_FOR errors in the logs.
- Why does n8n show Connection lost behind Nginx?
- The editor keeps a WebSocket open to n8n. If Nginx does not pass the Upgrade and Connection headers, that connection fails and the editor shows Connection lost. Add proxy_http_version 1.1 and the two upgrade headers to the location block.
- What does Cloudflare error 524 mean for n8n webhooks?
- Cloudflare waited 100 seconds for n8n to answer and gave up. On Free, Pro and Business plans that limit can't be raised. Make long workflows answer the webhook immediately and do the slow work afterwards.
Self-Hosting Setup or Fix · $200 fixed
Want it set up right the first time, with tested backups?
n8n, Ghost, Metabase, or Coolify / Dokploy on your own VPS. New install, or an existing one throwing 502s, restarting in a loop, or quietly filling the disk.
- Platform installed and locked down: firewall on, SSH password logins off, automatic panel updates off
- Your app live on your domain with SSL
- Nightly backups of your database and your files, stored off the server
- A real restore test before handover, with the time it took written down
If the restore test fails and I can't make it pass, you don't pay.
Not ready to buy? Send me what you plan to run and where. I'll reply with what I would do, in one message. Ask for a free recommendation. No call, no pitch.

DevOps & Cloud Consultant
I build systems that run reliably, scale efficiently, and deploy intelligently. See how I can help your team.