尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

NodeBB 4.14.8 Docker 登录失败解决:invalid csrf token / csrf-invalid / Socket.IO 403,最终竟是 trust_proxy

NodeBB 4.14.8 Docker 登录失败解决:invalid csrf token / csrf-invalid / Socket.IO 403,最终竟是 trust_proxy 前言最近使用 Docker 部署 NodeBB 时遇到了一个非常隐蔽的问题NodeBB 安装完成后网站页面可以正常访问但账号始终无法登录。浏览器提示登录失败 可能是由于会话过期登录失败。请重试。NodeBB 后端日志则出现POST /login invalid csrf token同时浏览器 Network 中还能看到/socket.io/?_csrfxxxxxEIO4transportpolling 403 Forbidden一开始很容易怀疑Cloudflare SSL 配置Nginx 反向代理WebSocketRedis Session浏览器 CookieNodeBB URLMongoDBHTTPS 证书但最终发现真正原因非常简单NodeBB Docker 实际使用的config.json中没有开启trust_proxy。加入trust_proxy: true重启 NodeBB 后登录立即恢复正常。下面记录完整排查过程和最终解决方法。一、问题环境环境大致如下NodeBB 4.14.x Docker MongoDB Redis Nginx 反向代理 HTTPS Cloudflare可选访问结构浏览器 ↓ HTTPS Cloudflare ↓ HTTPS Nginx ↓ HTTP NodeBB :4567NodeBB 本身运行在127.0.0.1:4567Nginx 负责 HTTPS 和反向代理。二、错误特征这个问题有几个非常典型的特征。1️⃣ NodeBB 页面可以正常打开访问https://example.com首页、登录页等都可以正常加载。这说明Nginx → NodeBB基本是通的。2️⃣ 登录时提示 Session 过期前端提示类似登录失败 可能是由于会话过期登录失败。请重试。英文版可能显示Login Unsuccessful We were unable to log you in, likely due to an expired session. Please try again.这个提示很容易让人误以为Cookie 过期了。但实际上未必如此。3️⃣ NodeBB 日志出现invalid csrf token执行docker logs nodebb --tail 100可以看到error: POST /login invalid csrf token或者浏览器最终跳转/login?errorcsrf-invalid这是最关键的错误特征。4️⃣ Socket.IO 返回 403浏览器开发者工具中可能同时出现GET /socket.io/?_csrfxxxxxxxxEIO4transportpolling 403 Forbidden因此很容易误判为WebSocket 被 Cloudflare 或 Nginx 阻断。实际上这里甚至还处在 Socket.IO 的 polling 阶段。所以Socket.IO 403很多时候只是CSRF / Session 错误的结果并不是 WebSocket 本身的问题。三、先确认 NodeBB 本身有没有坏可以直接绕过 Nginxcurl -I http://127.0.0.1:4567/login如果返回HTTP/1.1 200 OK X-Powered-By: NodeBB说明NodeBB 本体 ✅ 端口 4567 ✅ Docker 网络 ✅ HTTP 服务 ✅因此没有必要因为登录失败就重新安装 NodeBB。四、为什么清 Redis 没用因为出现invalid csrf token自然会想到 Session于是可以尝试docker exec -it redis redis-cli然后FLUSHDB再重启docker restart nodebb如果还是POST /login invalid csrf token那么问题大概率不是旧 Session。我这里清空 Redis 后问题依旧存在。五、为什么 Cloudflare 也不是根因最开始也怀疑 Cloudflare。尤其是如果使用Flexible SSL同时源站又强制 HTTPS很容易产生ERR_TOO_MANY_REDIRECTS因此正常情况下推荐Cloudflare SSL/TLS → Full或者Full (strict)但是即使临时将 Cloudflare DNS 改成DNS only让浏览器直接连接服务器NodeBB 登录仍然csrf-invalid那么 Cloudflare 基本就可以排除了。六、Nginx 反向代理看起来也是正常的常见 NodeBB Nginx 配置类似location / { proxy_pass http://127.0.0.1:4567; 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_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; }检查最终 Nginx 配置nginx -T | grep X-Forwarded-Proto -n如果能看到proxy_set_header X-Forwarded-Proto $scheme;说明 Nginx 确实已经向 NodeBB 发送了代理协议头。这里就引出了真正的问题Nginx 发了X-Forwarded-*但 NodeBB/Express 不一定信任它。七、真正根因trust_proxy没有开启这是本次问题的核心。在反向代理环境中NodeBB 后面的 Express 需要信任代理服务器。否则即使 Nginx 已经传递X-Forwarded-Proto X-Forwarded-For X-Forwarded-Host应用也可能无法按照预期使用这些信息。最终可能导致HTTPS 判断 ↓ Session ↓ Cookie ↓ CSRF Token之间状态不一致。表现出来就是POST /login invalid csrf token以及/socket.io/ 403八、新版 NodeBB Docker 的config.json在哪里这里也是一个坑。网上很多旧教程会让你找/usr/src/app/config.json但新版 Docker 部署不一定在这里。例如执行docker exec -it nodebb bash然后ls /usr/src/app可能根本没有config.json可以直接搜索docker exec nodebb sh -lc \ find /opt /usr/src/app -maxdepth 3 -name config.json -type f -print 2/dev/null我的 Docker 环境中实际找到的是/opt/config/config.json这才是 NodeBB 当前真正使用的配置文件。九、检查trust_proxy执行docker exec nodebb sh -lc \ grep -E \(url|trust_proxy)\ /opt/config/config.json问题发生时只显示url: https://example.com却没有trust_proxy: true这就是关键。十、最终解决方案1️⃣ 先备份配置不要直接修改。docker cp \ nodebb:/opt/config/config.json \ /root/nodebb-config.json.bak确认Successfully copied ...2️⃣ 写入trust_proxy: true可以直接使用 Node 修改 JSON避免手工编辑时把 JSON 格式弄坏docker exec -u 0 nodebb node -e const fsrequire(fs); const p/opt/config/config.json; const cJSON.parse(fs.readFileSync(p,utf8)); c.trust_proxytrue; fs.writeFileSync( p, JSON.stringify(c,null,4)\n ); 3️⃣ 检查配置执行docker exec nodebb sh -lc \ grep -E \(url|trust_proxy)\ /opt/config/config.json正常应该出现url: https://example.com, trust_proxy: true4️⃣ 重启 NodeBBdocker restart nodebb然后docker logs nodebb --tail 30如果修复成功可以看到非常关键的一行Setting trust proxy to true同时还应该有NodeBB Ready NodeBB is now listening on: 0.0.0.0:4567 Canonical URL: https://example.com这说明 NodeBB 已经真正读取到了trust_proxy: true十一、修改后的关键启动日志正常启动后类似Initializing NodeBB v4.14.x https://example.com [socket.io] Restricting access to origin: https\://example.com:\* NodeBB Ready Setting trust proxy to true NodeBB is now listening on: 0.0.0.0:4567 Canonical URL: https://example.com其中最关键的就是Setting trust proxy to true如果没有这一行就需要继续确认 NodeBB 到底读取的是哪个config.json。十二、最后清理浏览器 Session由于之前产生过错误 Session修复后建议删除一次网站 Cookie。或者直接使用浏览器无痕模式访问https://example.com然后重新登录。修复后/login即可正常登录。Socket.IO 也会恢复连接。十三、emoji 插件报错和登录失败无关排查过程中还可能看到[emoji] Failed to retrieve data for parse ENOENT: no such file or directory或者Error: EACCES: permission denied, mkdir /usr/src/app/public/uploads/emoji例如nodebb-plugin-emoji相关错误。这些是Emoji 插件文件或权限问题。它们需要单独处理但不是invalid csrf token的根本原因。因此不要因为看到一大串红色日志就把注意力全部放到 emoji 插件。十四、完整排查思路如果你也遇到NodeBB 登录失败 Session expired invalid csrf token csrf-invalid Socket.IO 403建议按照这个顺序排查1. curl 127.0.0.1:4567/login ↓ 确认 NodeBB 本体正常 2. 检查 Nginx 反向代理 ↓ X-Forwarded-Proto Host WebSocket 3. Cloudflare 临时灰云 ↓ 排除 CF 4. 无痕浏览器 ↓ 排除旧 Cookie 5. 清 Redis Session ↓ 排除旧 Session 6. 找 NodeBB 真正使用的 config.json ↓ /opt/config/config.json 7. 检查 trust_proxy ↓ trust_proxy: true 8. 重启 NodeBB ↓ 日志出现 Setting trust proxy to true十五、最终结论这次最误导人的地方在于网页可以正常打开所以看起来 NodeBB、Nginx、HTTPS 全部正常。实际上普通 HTTP 页面请求和Session / Cookie / CSRF不是同一层问题。Nginx 虽然已经将X-Forwarded-Proto传给 NodeBB但是如果 NodeBB 没有trust_proxy: true代理信息无法按照预期被应用层信任最终就可能出现登录失败 ↓ invalid csrf token ↓ Socket.IO 403最终只需要在 NodeBB 真正使用的/opt/config/config.json中加入trust_proxy: true然后docker restart nodebb问题即可解决。
返回列表