You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Puma的Rails API如何同时兼容XHR与WebSocket?

解决Rails API(Puma)同时支持XHR和WebSocket的问题

听起来你切换到Puma后碰到了WebSocket正常但XHR请求返回空响应的麻烦,我来帮你一步步排查解决:

一、检查Rails API模式的中间件配置

Rails API模式默认会精简中间件,但部分关键中间件缺失可能导致XHR响应异常。打开config/application.rb,确认配置没有遗漏必要项:

class Application < Rails::API
  # 确保Action Cable的路径配置正确
  config.action_cable.mount_path = '/cable'
  # 跨域场景下可能需要关闭CSRF保护,注意根据实际安全需求调整
  config.action_cable.disable_request_forgery_protection = true

  # 确认包含处理XHR响应的核心中间件
  config.middleware.use ActionDispatch::Cookies
  config.middleware.use ActionDispatch::Session::CookieStore
end

另外,检查你的控制器逻辑,确保XHR请求的处理方法里明确调用了render json: ...或对应响应语句,不要遗漏返回内容。

二、调整Puma配置

Puma原生支持同时处理HTTP和WebSocket,但错误配置可能干扰XHR请求。打开config/puma.rb,核对以下要点:

# 监听端口或Unix Socket(和Nginx通信推荐用Socket)
port ENV.fetch("PORT") { 3000 }
# bind "unix://#{Rails.root}/tmp/sockets/puma.sock"

# 合理设置线程和进程数,避免资源限制
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# 不要启用tcp_mode,否则会跳过HTTP解析,导致WebSocket升级失败、XHR异常
# 禁止添加:tcp_mode true

preload_app!

on_worker_boot do
  ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
end

三、关键:Nginx反向代理配置调整

这是最容易出问题的环节!Nginx需要同时处理普通HTTP(XHR)和WebSocket的协议升级请求,以下是适配的示例配置:

server {
  listen 80;
  server_name your-domain.com;

  # 前端静态文件由Nginx直接处理
  root /path/to/your/frontend/dist;
  index index.html;

  # 处理WebSocket请求:匹配Action Cable路径,触发协议升级
  location /cable {
    proxy_pass http://localhost:3000; # 用Socket的话替换为:unix:///path/to/rails-app/tmp/sockets/puma.sock
    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-Proto $scheme;
    proxy_cache_bypass $http_upgrade;
  }

  # 处理所有API请求(XHR),替换成你的API路径前缀
  location ~ ^/(api|users|posts) {
    proxy_pass http://localhost:3000; # 同样可替换为Socket地址
    proxy_http_version 1.1;
    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;
    # 避免XHR响应被缓存
    proxy_cache_bypass $http_pragma;
    proxy_cache_revalidate on;
  }

  # SPA前端路由 fallback
  location / {
    try_files $uri $uri/ /index.html;
  }
}

重点注意:

  • WebSocket的location /cable必须设置proxy_http_version 1.1和Upgrade/Connection头,这是协议升级的关键。
  • 确保API请求的location规则不会被静态文件规则覆盖,避免Nginx误把XHR请求当成静态资源处理。

四、调试排查步骤

如果以上配置调整后仍有问题,可以通过以下方式定位:

  1. 直接访问Puma端口(如http://localhost:3000/api/your-endpoint),验证XHR请求是否正常。如果直接访问正常,问题大概率在Nginx配置。
  2. 查看Rails日志(log/production.log或log/development.log),确认XHR请求是否到达后端、有没有报错信息。
  3. 查看Nginx错误日志(/var/log/nginx/error.log),排查代理连接相关的异常。
  4. 用浏览器开发者工具查看XHR请求的状态码和响应头,确认是200空响应还是其他状态码(比如404路由匹配错误、502 Puma连接失败)。

内容的提问来源于stack exchange,提问作者Andre Song

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:23:33