自建 n8n 故障排查与修复全记录

目录

1. 背景知识

n8n 是一款强大的开源工作流自动化工具,采用前后端分离的架构。浏览器(客户端 Web UI)与 n8n 后端服务器之间需要保持高频、实时的双向数据通信,用于同步节点执行状态、刷新运行日志。这种实时通信极度依赖 WebSocket 协议(或 Server-Sent Events)。

当我们在自建环境(如 Docker、PM2)中部署 n8n 并通过 Nginx 等反向代理暴露服务时,如果网络链路、代理配置或环境变量出现错位,极易引发前后端断连,导致前端页面频繁弹出错误提示。

2. 问题现象

自建部署的 n8n 正常访问后,在开发、测试或执行 Workflow(工作流)时,前端浏览器频繁弹出以下报错弹窗,导致工作流无法正常调试或执行:

错误提示:

You have a connection issue or the server is down. n8n should reconnect automatically once the issue is resolved.

同时,查看 n8n 服务端的启动与运行日志,伴随有大量连续的底层网络请求连接失败报错:

2026-06-12T11:01:40.006Z [Rudder] error: Error: ECONNREFUSED
connect ECONNREFUSED 127.0.0.1:443
Error: connect ECONNREFUSED 127.0.0.1:443
    at AxiosError.from (.../axios/lib/core/AxiosError.js:77:24)
    at RedirectableRequest.handleRequestError (.../axios/lib/adapters/http.js:1161:27)
    ...
connect ECONNREFUSED 127.0.0.1:443
Error: connect ECONNREFUSED 127.0.0.1:443
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1705:16)

3. 分析与定位

通过对前端表现、Nginx 配置以及服务端启动日志的交叉对比,该故障主要由以下两组深层原因复合导致:

3.1 Nginx 反向代理中的 Host 端口丢失与 WebSocket 阻塞

在原始的 Nginx 配置文件中,服务监听的是非标准端口(如 50005),但在 location / 代理块中使用了如下配置:

proxy_set_header Host $host;
  • 定位分析​:Nginx 的内建变量 $host仅包含域名,不包含端口号​(即 example.com)。当 Nginx 将请求转发给后端的 n8n 时,Host 头丢失了 50005 端口。
  • 连锁反应​:浏览器发起 WebSocket 握手时携带的是 example.com:50005,而 n8n 后端收到的却是 example.com。这种不一致会直接触发浏览器的跨域限制(CORS)或安全校验失败,导致 WebSocket 瞬间断开,触发前端 Connection issue 弹窗。

3.2 遥测模块流量闭环引发的本地 127.0.0.1:443 碰撞

从日志中带有 [Rudder][PostHog] 标记的报错可以看出,这是 n8n 内置的诊断与遥测收集模块在向官方服务器发送心跳数据。

  • 定位分析​:在私有化部署环境下,如果服务器或容器内部配置了系统代理(如 HTTP_PROXY / HTTPS_PROXY 指向本地回环),或者受特定 Host 文件解析影响,这些本该发往外网的 HTTPS 请求被强制扭转到了本地的 127.0.0.1:443。由于本地 443 端口并未开放,直接导致 Axios 报错 ECONNREFUSED,高频的连接失败阻塞了 Node.js 的事件循环,间接拖垮了正常的 Webhook 路由与前端长连接。

4. 原因总结

  1. 反向代理 Host 头不完整​:在非标准端口部署时,使用 $host 导致端口丢失,引发 WebSocket 跨域断连。
  2. 环境代理污染或未闭环的遥测请求​:内置的 RudderStack 模块请求外网时被本地环境拦截/扭转至 127.0.0.1:443,因无监听服务而引发网络层崩溃。

5. 解决方案

5.1 修正并规范 Nginx 配置文件

使用 $http_host 代替 $host,确保端口信息完整传递;同时将散落的代理响应头规范化并延长超时时间。修改后的标准 Nginx 配置文件如下:

server {
    listen 50005 ssl; 
    server_name example.com; 
    index index.php index.html index.htm default.php default.htm default.html; 
    access_log /www/sites/n8n/log/access.log main; 
    error_log /www/sites/n8n/log/error.log; 

    # 开启 HTTP2 支持
    http2 on; 

    # SSL 证书配置
    ssl_certificate /www/sites/n8n/ssl/fullchain.pem; 
    ssl_certificate_key /www/sites/n8n/ssl/privkey.pem; 
    ssl_protocols TLSv1.3 TLSv1.2; 
    ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED; 
    ssl_prefer_server_ciphers off; 
    ssl_session_cache shared:SSL:10m; 
    ssl_session_timeout 10m; 

    # HSTS 安全响应头
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; 

    # 针对非标准端口的 HTTP 自动跳转 HTTPS
    error_page 497 https://$http_host$request_uri; 

    # 屏蔽敏感文件
    location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md) {
        return 404; 
    }

    # 证书续签通道
    location ^~ /.well-known/acme-challenge {
        allow all; 
        root /usr/share/nginx/html; 
    }

    if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
        return 403; 
    }

    # 核心反向代理块
    location / {
        # 【关键修改】使用 $http_host 确保传递 50005 端口,防止 CORS 跨域限制
        proxy_set_header Host $http_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 https; 
        proxy_set_header X-Forwarded-Host $http_host; 
        
        # WebSocket 支持核心配置
        proxy_http_version 1.1; 
        proxy_set_header Upgrade $http_upgrade; 
        proxy_set_header Connection "upgrade"; 
        
        # 延长超时时间,防止长连接被 Nginx 主动断开
        proxy_connect_timeout 60s;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;

        # 后端 n8n 实际运行端口
        proxy_pass http://127.0.0.1:5678; 
    }
}

5.2 调整 n8n 环境变量

为了彻底杜绝未知的外部网络碰撞,并在非标准端口下保证 Webhook 路径的正确生成,需在 n8n 部署环境(如 docker-compose.yml.env 文件)中追加、修正以下环境变量:

# 确保 WEBHOOK_URL 包含完整的协议、域名及非标准端口
WEBHOOK_URL=https://example.com:50005/

# 彻底禁用官方诊断与遥测数据收集,防止 RudderStack 撞墙报错
N8N_DIAGNOSTICS_ENABLED=false
N8N_TELEMETRY_ENABLED=false

# 显式清除潜在的容器内系统代理干扰
HTTP_PROXY=""
HTTPS_PROXY=""
NO_PROXY="localhost,127.0.0.1"

修改完成后,通过命令行重载服务使配置生效:

# 检查并重载 Nginx
nginx -t && nginx -s reload

# 重启 n8n 容器或服务
docker compose down && docker compose up -d

6. 结果

配置更新并重载后,服务端启动日志中不再输出 connect ECONNREFUSED 127.0.0.1:443 的阻塞异常。

使用浏览器登录 n8n Web UI,WebSocket 成功通过 Nginx 完成协议升级并建立持久的实时连接。在长时间编辑、调试及手动触发执行工作流(Workflow)时,前端页面不再弹出 You have a connection issue 报错,系统恢复稳定运行。

7. 总结

在非标准端口下私有化部署 n8n 时,反向代理的细节对服务的稳定性有着决定性的影响。对于非标准端口,Nginx 配置中切记使用 $http_host 代替 $host 以保留端口信息。同时,在企业或私有网络环境下,主动关闭不必要的官方遥测模块(N8N_TELEMETRY_ENABLED=false),可以有效规避很多由复杂网络边界导致的底层闭环报错。