HAProxy→Nginx/Passenger→Rails链路WebSocket连接失败排查求助
生产环境Action Cable WebSocket连接失败(404/1006)排查方案
核心问题定位
从Nginx日志提示尝试读取静态文件/opt/webapps/app/current/public/cable来看,Nginx没有将/cable请求转发给Rails应用,而是误将其当作静态资源处理,这是导致404的直接原因。结合你的架构(HAProxy→Nginx/Passenger→Rails),按以下层级逐一排查:
1. 修正Nginx/Passenger配置,确保/cable路由到Rails
在Nginx的server配置块中,添加专门的location /cable规则,覆盖静态文件处理逻辑,强制将请求转发给Rails的Action Cable端点:
server { # 其他原有配置... # 针对Action Cable的WebSocket请求配置 location /cable { passenger_app_root /opt/webapps/app/current; passenger_ruby /path/to/your/ruby; # 替换为实际Ruby路径 passenger_enabled on; # WebSocket升级必需的头信息 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-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; # 禁用Passenger的并发请求限制(避免WebSocket连接被中断) passenger_force_max_concurrent_requests_per_process 0; } # 原有静态文件处理规则(确保在/cable规则之后) location ~ ^/assets/ { expires 1y; add_header Cache-Control public; # ...其他静态资源配置 } }
注意:
location /cable必须放在静态资源处理规则之前,否则Nginx会优先匹配静态文件规则。
2. 验证HAProxy的WebSocket转发配置
HAProxy需要正确识别并转发WebSocket升级请求的头信息,在HAProxy配置中添加以下规则:
frontend https_frontend bind *:443 ssl crt /path/to/cert.pem # 其他原有配置... # 识别WebSocket请求 acl is_websocket hdr(Upgrade) -i websocket acl is_connection_upgrade hdr(Connection) -i upgrade # 转发WebSocket必需的头 reqadd X-Forwarded-Proto:\ https if { ssl_fc } reqadd Upgrade:\ websocket if is_websocket reqadd Connection:\ upgrade if is_websocket # 超时设置(避免WebSocket连接被过早断开) timeout client 86400s timeout server 86400s # 转发到后端Nginx服务器 default_backend nginx_backend backend nginx_backend # 其他原有配置... option http-server-close # 确保HTTP连接正确处理升级 timeout tunnel 86400s # WebSocket隧道超时
3. 确认Rails端的Action Cable配置
- 检查
config/routes.rb是否已正确挂载Action Cable:Rails.application.routes.draw do mount ActionCable.server => '/cable' # 其他路由... end - 确认
config/cable.yml的生产环境配置指向正确的Redis实例:production: adapter: redis url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> channel_prefix: app_production_cable - 检查
config/environments/production.rb中的Action Cable相关设置:config.action_cable.url = "wss://app.host.com/cable" config.action_cable.allowed_request_origins = ["https://app.host.com"] # 匹配前端域名 config.action_cable.disable_request_forgery_protection = false # 保持默认,除非有特殊需求
4. 检查Passenger兼容性
确保Passenger版本≥5.0.0(Action Cable需要Passenger支持WebSocket),运行以下命令验证版本:
passenger -v
如果版本过低,升级到最新稳定版:
# 针对Nginx模块的Passenger升级 sudo passenger-install-nginx-module
5. 用curl模拟WebSocket升级请求验证
执行以下命令测试升级流程,观察返回状态码:
curl -i -N \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Host: app.host.com" \ -H "Origin: https://app.host.com" \ -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \ -H "Sec-WebSocket-Version: 13" \ https://app.host.com/cable
- 如果返回
101 Switching Protocols,说明升级成功,问题出在前端配置; - 如果仍返回404,继续检查Nginx/HAProxy的路由规则;
- 如果返回其他错误,根据响应头排查权限或头传递问题。
6. 排除SSO/VPN干扰
- 确认Okta SSO代理没有过滤
Upgrade和Connection头,可联系SSO管理员检查代理规则; - 验证VPN是否允许WebSocket流量(虽然443端口通常开放,但部分VPN可能拦截WebSocket握手请求)。
内容的提问来源于stack exchange,提问作者7minutesdead
相关产品推荐
相关产品推荐

