问题

Web 应用中,需要重定向时,服务端返回一个 301 或者 302 的响应即可,例如在一个 Rails 应用中重定向到登录页面:

unless user_signed_up?
  redirect_to login_path
end

此时能根据当前主机和端口,自动返回正确的重定向地址给前端,前端再向服务器发送请求。

但是当部署到服务器,应用通过 socket 方式启动,然后配置 Nginx 绑定端口和域名对外开放访问时,所有的跳转都会丢失端口号,使用默认的80端口跳转。 如果此时你主机配置的不是 80 端口,就会产生问题。

下面是节选的 nginx 和 puma 配置:

# Nginx
upstream puma_sample.conf {
  server unix:/opt/rails_app/sample/shared/tmp/sockets/puma.sock fail_timeout=0;
}

server {
  listen 9999;
  root /opt/rails_app/sample/current/public;
  try_files $uri/index.html $uri @puma_sample.conf;

  location @puma_sample.conf {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_redirect off;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header X-Forwarded-Proto http;
    proxy_pass http://puma_sample.conf;
    # limit_req zone=one;
    access_log /opt/rails_app/sample/shared/log/nginx.access.log;
    error_log /opt/rails_app/sample/shared/log/nginx.error.log;
  }
  ...
# Puma 配置
#!/usr/bin/env puma

directory '/opt/rails_app/sample/current'
rackup "/opt/rails_app/sample/current/config.ru"
stdout_redirect '/opt/rails_app/sample/shared/log/puma_access.log', '/opt/rails_app/sample/shared/log/puma_error.log', true
bind 'unix:///opt/rails_app/sample/shared/tmp/sockets/puma.sock'

此时发送重定向请求时,会丢失 9999 端口设置,而使用 80 端口跳转。

解决

解决的话,需要修改 Nginx 的主机 header 设置,给主机 header 信息添加当前端口:

proxy_set_header Host $host:$server_port; # 保留端口跳转

这样修改 Nginx 后,就能响应正常了。