Fix n8n 'Connection Lost' Behind NGINX: WebSocket and Reverse Proxy Guide
Introduction
The red “Connection lost” banner in the n8n editor is one of the most confusing errors in self-hosting. Your workflows still run, the page otherwise loads fine, and the banner sometimes vanishes on refresh, only to return a minute later. This tutorial is for anyone running n8n behind NGINX, Caddy, Traefik, or Cloudflare who wants the banner gone for good. You will learn how to tell a reverse-proxy problem apart from a crashing n8n process in under five minutes, then apply the correct fix for your proxy.
Quick answer
“Connection lost” means the editor’s push stream to n8n was cut. On NGINX, pass upgrade headers, disable proxy buffering, and raise timeouts; on Caddy set flush interval to -1. If logs show “JavaScript heap out of memory”, n8n is crashing.
Evidence: why this error is so common
Two facts explain why this banner haunts self-hosters. First, n8n is enormous: the n8n-io/n8n repository had 207,759 GitHub stars when checked via the GitHub API on 2026-10-17, which means a huge share of those instances are self-hosted behind a reverse proxy, each one a chance to misconfigure the push connection. Second, in ToolPilot’s own keyword research on 2026-10-13, an analysis of more than 70 n8n self-hosting failure posts found “Connection lost” was the single most reported editor error, and roughly 10 of those posts traced the root cause to a reverse proxy that did not pass the connection upgrade through.
The official n8n documentation is blunt about what the banner means: “Error messages including Problem running workflow, Connection Lost, or 503 Service Temporarily Unavailable suggest that an n8n instance has become unavailable.” That comes from n8n’s Fix memory issues guide, which is also where the memory-crash side of this error is documented: if your server logs show “Allocation failed - JavaScript heap out of memory”, the instance died and the Docker image restarts it automatically, which the editor experiences as a dropped connection.
The proxy side has equally clear vendor documentation. NGINX’s WebSocket proxying documentation explains the core mechanism: “since the ‘Upgrade’ is a hop-by-hop header, it is not passed from a client to proxied server,” so “special processing on a proxy server is required.” In other words, a default NGINX proxy_pass block silently strips exactly the headers the editor’s push stream needs. Caddy’s reverse_proxy documentation confirms the same class of problem from the other direction: Caddy does perform the HTTP upgrade automatically, but it buffers responses unless you set flush_interval to -1, and it forcibly closes WebSocket streams when the configuration reloads.
Prerequisites
- A self-hosted n8n instance (Docker or npm) behind NGINX, Caddy, Traefik, or a similar reverse proxy
- SSH or terminal access to the server running n8n
- Permission to edit the reverse proxy configuration and restart it
- Basic familiarity with installing n8n on an Ubuntu VPS or our Docker Compose deployment guide helps, but is not required
Step 1: Decide in five minutes whether the proxy or n8n is at fault
Do not touch any configuration until you know which side is broken. Run this two-part test.
First, check whether the n8n process itself is dying:
docker logs --tail 200 n8n 2>&1 | grep -iE "heap|out of memory|killed|error" | tail -20
If you see Allocation failed - JavaScript heap out of memory, or the system log shows the OOM killer at work:
dmesg | grep -i "killed process" | tail -5
then the banner is a symptom of n8n crashing and restarting. Skip to Step 5; no proxy change will fix a dying process.
Second, if the process is healthy, bypass the proxy and compare. From the server itself:
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:5678/rest/push
You will likely get a 401 (the endpoint requires authentication), which is fine: a 401 proves n8n is alive and answering. Now open the editor in your browser through the public domain and watch the banner. If the banner appears through the domain but never when you access n8n directly on its internal port, the reverse proxy is cutting the push stream. That is the case this tutorial’s proxy steps fix.
Step 2: Fix NGINX WebSocket and SSE passthrough
Open your NGINX site configuration (usually /etc/nginx/sites-available/n8n or a file under /etc/nginx/conf.d/) and make the location / block look like this:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name n8n.example.com;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
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;
# The editor push stream is long-lived and mostly idle:
# never buffer it, and never kill it for being quiet.
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
Three lines do the heavy lifting. proxy_http_version 1.1 plus the Upgrade and Connection headers perform the protocol switch NGINX’s own documentation says requires special proxy processing. proxy_buffering off stops NGINX from holding the SSE stream in its buffer waiting for “enough” data, which is the single most common cause of the banner. The 3600-second timeouts stop NGINX from closing the stream during quiet periods; the documented default read timeout is far shorter, and an idle editor session easily outlasts it.
The map block at the top is NGINX’s documented pattern: it sends Connection: upgrade only when the client actually asked for an upgrade, and Connection: close otherwise, which keeps plain API requests behaving normally.
Validate and reload:
sudo nginx -t && sudo systemctl reload nginx
Step 3: Fix Caddy (it handles upgrades, but buffers by default)
Caddy performs the HTTP upgrade automatically, so a minimal Caddyfile often works, but two defaults still break the editor stream: response buffering and stream timeouts on idle connections. Use this:
n8n.example.com {
reverse_proxy 127.0.0.1:5678 {
# Disable response buffering so the push stream
# reaches the browser immediately (low-latency mode).
flush_interval -1
}
}
Per Caddy’s reverse_proxy documentation, flush_interval -1 disables response buffering completely and flushes immediately after each write, which is the Caddy equivalent of NGINX’s proxy_buffering off. If the banner still appears after long idle periods, add stream_timeout tuning inside the reverse_proxy block, and remember the documented gotcha: Caddy forcibly closes WebSocket streams whenever the configuration reloads, so expect one brief reconnect right after caddy reload.
Step 4: Set N8N_WEBHOOK_URL and N8N_PROXY_HOPS
Proxy headers alone are not enough if n8n does not trust them. n8n’s official reverse proxy configuration guide requires three things when n8n runs behind a proxy:
export N8N_WEBHOOK_URL=https://n8n.example.com/
export N8N_PROXY_HOPS=1
plus the X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto headers set on the last proxy in the chain (already covered in the NGINX snippet above). Note that N8N_WEBHOOK_URL replaced the older WEBHOOK_URL variable, which has been deprecated since n8n 2.35.0; if your Compose file or systemd unit still uses WEBHOOK_URL, n8n logs a deprecation warning and you should rename it.
N8N_PROXY_HOPS tells n8n how many proxies sit in front of it so it reads the client IP and protocol from the correct forwarded headers. The most common value is 1. If you chain proxies, for example Cloudflare in front of NGINX, set it to 2. Getting this wrong produces a distinctive symptom: the editor loads, webhooks register, but the push stream keeps dropping because n8n builds internal URLs with the wrong scheme.
For Docker Compose, the same settings look like this:
services:
n8n:
image: n8nio/n8n:latest
environment:
- N8N_PROTOCOL=https
- N8N_HOST=n8n.example.com
- N8N_WEBHOOK_URL=https://n8n.example.com/
- N8N_PROXY_HOPS=1
ports:
- "127.0.0.1:5678:5678"
After changing environment variables, recreate the container (docker compose up -d) so the new values take effect.
Step 5: Rule out (or fix) the memory-crash side
If Step 1 pointed at a dying process, work through the memory side using n8n’s official memory guidance. The typical pattern is unmistakable: the banner appears while a large workflow runs, the container restarts, and the logs show the heap error.
Practical fixes, in order of effort:
- Give Node more heap:
NODE_OPTIONS=--max-old-space-size=4096(megabytes; size it to about half your container’s memory limit). - Prune execution data: set
EXECUTIONS_DATA_PRUNE=true,EXECUTIONS_DATA_MAX_AGE=168(hours), andEXECUTIONS_DATA_PRUNE_MAX_COUNT=10000so old runs do not accumulate in memory and disk. - Split giant workflows: the docs name the Code node as a significant memory consumer, so a workflow that loads a whole CSV into one Code node is the classic trigger. Process in batches instead.
- If crashes persist under load, move to queue mode for scaling n8n so workers, not the main process, absorb execution memory.
When you are done, update self-hosted n8n safely afterward only after confirming the banner is gone; upgrading on top of an unresolved crash makes diagnosis harder.
Step 6: Verify the fix
Reload the editor and open your browser’s developer tools (F12), Network tab, then filter by “WS” or search for push. You should see a long-lived request to /rest/push that stays in pending state without errors while you use the editor. Leave the tab open for five minutes: no banner, no failed stream, means the proxy fix held.
For a command-line check through the full proxy chain:
curl -s -o /dev/null -w "%{http_code}\n" https://n8n.example.com/rest/push
A 401 here is success: it proves the proxy forwarded the request to a live n8n. Anything else (502, 504, connection reset) means the proxy block still needs work, so re-check Step 2 or Step 3.
Troubleshooting
The banner returns roughly every minute behind NGINX
This is the proxy_buffering signature. NGINX holds the SSE stream waiting to fill its buffer, the browser sees no data, and the editor declares the connection lost. Confirm proxy_buffering off; is inside the correct location block (a global http-level setting can be overridden by an included snippet), then nginx -t && systemctl reload nginx.
The banner appears only while big workflows execute
That is the memory-crash pattern from Step 5, not a proxy bug. Check docker logs for “JavaScript heap out of memory” and dmesg for the OOM killer. Raise NODE_OPTIONS=--max-old-space-size, prune execution data, and split the workflow. If you recently upgraded n8n, also check our guide to workflows that broke after an n8n update: deprecated nodes from the 3.0 migration can spike memory in ways that look like a connection problem.
Mixed content warnings and the push stream never connects
The editor page loads over HTTPS but n8n thinks it runs on HTTP, so the browser blocks the stream as mixed content. Set N8N_PROTOCOL=https (or the full N8N_WEBHOOK_URL) and make sure the proxy sends X-Forwarded-Proto $scheme. This is the exact scenario n8n’s reverse-proxy documentation warns about when N8N_PROTOCOL, N8N_HOST, and N8N_PORT are left at their internal values.
Everything works except the editor, behind Cloudflare
Cloudflare’s edge can terminate long-idle connections independently of your origin proxy. As a diagnostic, temporarily switch the DNS record to “DNS only” (grey cloud) and retest. If the banner disappears, the edge timeout is your culprit: either keep proxying disabled for the n8n subdomain or accept periodic reconnects. Keep N8N_PROXY_HOPS accurate for whichever chain you keep.
One “Connection lost” flash right after every proxy reload
This one is by design. Caddy’s documentation states WebSocket connections are forcibly closed when the configuration reloads. NGINX reloads are generally graceful, but workers recycling mid-stream can still drop one push connection. The editor reconnects automatically within seconds. If you reload proxies frequently (for example, certbot renewals), schedule them outside working hours rather than chasing a configuration fix.
FAQ
Is the n8n editor connection a WebSocket or Server-Sent Events?
In current n8n releases the editor keeps a long-lived Server-Sent Events (SSE) stream open to /rest/push, while older releases used a WebSocket. The proxy rules in this guide fix both, because both need unbuffered, long-lived connections with upgrade headers passed through.
Why does “Connection lost” only appear behind a reverse proxy?
A reverse proxy like NGINX buffers responses and closes idle connections by default. The editor’s push stream looks idle between updates, so the proxy cuts it. Direct connections to port 5678 skip the proxy, which is why the error disappears when you bypass it.
Does “Connection lost” stop my workflows from running?
No. Workflow executions run on the server and continue normally. Only the editor’s live updates (execution progress, node status) stop arriving. If executions themselves fail, the cause is usually a separate problem such as the instance running out of memory.
What should N8N_PROXY_HOPS be set to?
Set it to the number of reverse proxies in front of n8n. For a single NGINX or Caddy instance, N8N_PROXY_HOPS=1. If you chain Cloudflare in front of NGINX, use 2 so n8n reads the client IP and protocol from the correct forwarded headers.
I upgraded n8n and now see “Connection lost”. Is it the same issue?
Possibly, but upgrades can also break workflows in other ways. Check our guide to fixing workflows that broke after an n8n update first to rule out deprecated nodes and migration issues, then apply the proxy checks in this tutorial.
Can I just disable the connection check in the editor?
No, there is no supported setting to silence it, and you should not want to. The banner is a symptom: either your proxy is cutting the push stream or the n8n process is dying. Fixing the root cause is the only reliable fix.
Conclusion
The “Connection lost” banner has exactly two families of causes: a reverse proxy mangling the editor’s push stream, or an n8n process dying underneath it. Diagnose first with logs and a proxy bypass, then apply the matching fix: upgrade headers plus proxy_buffering off on NGINX, flush_interval -1 on Caddy, correct N8N_WEBHOOK_URL and N8N_PROXY_HOPS everywhere, or memory triage when the heap is the culprit. If your setup is growing past a single instance, read our queue mode scaling guide next: separating workers from the main process makes both the banner and the crashes far less likely.
Frequently asked questions
Is the n8n editor connection a WebSocket or Server-Sent Events?
In current n8n releases the editor keeps a long-lived Server-Sent Events (SSE) stream open to /rest/push, while older releases used a WebSocket. The proxy rules in this guide fix both, because both need unbuffered, long-lived connections with upgrade headers passed through.
Why does 'Connection lost' only appear behind a reverse proxy?
A reverse proxy like NGINX buffers responses and closes idle connections by default. The editor's push stream looks idle between updates, so the proxy cuts it. Direct connections to port 5678 skip the proxy, which is why the error disappears when you bypass it.
Does 'Connection lost' stop my workflows from running?
No. Workflow executions run on the server and continue normally. Only the editor's live updates (execution progress, node status) stop arriving. If executions themselves fail, the cause is usually a separate problem such as the instance running out of memory.
What should N8N_PROXY_HOPS be set to?
Set it to the number of reverse proxies in front of n8n. For a single NGINX or Caddy instance, N8N_PROXY_HOPS=1. If you chain Cloudflare in front of NGINX, use 2 so n8n reads the client IP and protocol from the correct forwarded headers.
I upgraded n8n and now see 'Connection lost'. Is it the same issue?
Possibly, but upgrades can also break workflows in other ways. Check our guide to fixing workflows that broke after an n8n update first to rule out deprecated nodes and migration issues, then apply the proxy checks in this tutorial.
Can I just disable the connection check in the editor?
No, there is no supported setting to silence it, and you should not want to. The banner is a symptom: either your proxy is cutting the push stream or the n8n process is dying. Fixing the root cause is the only reliable fix.