为什么你的Nginx会无限308重定向?
今天给一个私有化项目排查故障,问题表现是请求会返回无限308重定向,
最后定位是因为Nginx开启了强制HTTPS的重定向而未做判断。借这个机会,把Nginx的无限重定向的原理和处理方法给大家做下分享。
什么是 308 状态码?
308 Permanent Redirect 是 HTTP/1.1 协议中引入的状态码,与 301 类似但有一个关键区别:
- 301/302:重定向返回可能会被浏览器将原本的 POST 请求改为 GET 请求
- 307/308:严格保持请求方法和请求体不变
- 307 = 临时重定向(对应302)
- 308 = 永久重定向(对应301)
出现无限 308 重定向的常见场景
场景一:HTTPS 强制跳转配置错误
这是最常见的原因!
# 错误配置 - 无限重定向 server { listen 80; listen 443 ssl; server_name example.com; # 问题:没有判断是否已经是 HTTPS return 308 https://example.com$request_uri; }问题原因:这个配置对 HTTP 和 HTTPS 请求都返回 308 重定向到 HTTPS,导致 HTTPS 请求也不断重定向自己。
正确配置:
server { listen 80; server_name example.com; return 308 https://$host$request_uri; } server { listen 443 ssl; server_name example.com; # HTTPS 业务配置... }场景二:CDN/负载均衡 + Nginx 配合问题
当请求经过 CDN 或负载均衡器后,HTTPS协议可能在CDN边缘节点已经终止,Nginx 收到的是 HTTP 请求,如果Nginx还是下面配置,就可能导致重定向:
# 错误配置 server { listen 80; server_name example.com; if ($scheme = "http") { return 308 https://$host$request_uri; } }问题:用户使用HTTPS访问CDN,CDN回源到后端Nginx使用 HTTP,导致 Nginx 不断返回 308 让 CDN 跳转。
解决方法:信任X-Forwarded-Proto头:
server { listen 80; server_name example.com; # 客户端是HTTP才重定向,CDN是HTTP就不重定向了 if ($http_x_forwarded_proto != "https") { return 308 https://$host$request_uri; } }场景三:多location块配合错误
# 错误配置 location /docs/ { rewrite ^/docs/(.*)$ /documentation/$1 permanent; } location /documentation/ { rewrite ^/documentation/(.*)$ /docs/$1 permanent; }如果Nginx的配置文件由多人维护,这种情况出现频率还是很高的。
场景四:www 与非 www 互相重定向
# 错误配置 server { listen 443 ssl; server_name example.com www.example.com; # 非 www 转 www if ($host = "example.com") { return 308 https://www.example.com$request_uri; } # 又不小心写了反向规则! if ($host = "www.example.com") { return 308 https://example.com$request_uri; } }检查方法:用 curl 测试看 Location 头:
# 输出中看 Location: 是否不断变化curl-Ihttps://example.com场景五:trailing slash(末尾斜杠)配置冲突
# 错误配置 server { # 加斜杠 rewrite ^([^.]*[^/])$ $1/ permanent; # 又去斜杠 rewrite ^/(.*)/$ /$1 permanent; }两个规则互相打架!这种情况相对比较少见。
上面是常见的导致无限重定向的配置错误,下面谈下如何诊断此类故障。
诊断无限重定向的方法
curl 跟踪重定向
# 只看头部curl-Ihttp://example.com# 跟踪最多5次重定向curl-IL--max-redirs5http://example.com查看 Nginx 访问日志
# 看同一个 IP 短时间内的请求模式tail-f/var/log/nginx/access.log临时改成 307 调试
之所以要这样改,是因为308重定向会被浏览器缓存,影响我们对故障的排查。
# 调试时先用临时重定向 return 307 https://...; # 或 302 rewrite ... redirect; # 不要用 permanent常用重定向场景实战
场景:www 与非 www 互转
非 www 转 www:
server { listen 443 ssl; server_name example.com; return 308 https://www.example.com$request_uri; }www 转非 www:
server { listen 443 ssl; server_name www.example.com; return 308 https://example.com$request_uri; }场景:文件扩展名隐藏
# 访问 /about → 实际读取 /about.html location / { try_files $uri $uri.html $uri/ =404; }场景:整站域名迁移
server { listen 80; listen 443 ssl; server_name old-domain.com www.old-domain.com; # 所有请求转到新域名,保持路径 return 301 https://new-domain.com$request_uri; }场景:特定目录重定向
location ^~ /docs/v1/ { # 旧版本文档转到新版本 return 301 /docs/latest/; }场景:移动端适配跳转
set $mobile_rewrite do_not_rewrite; if ($http_user_agent ~* "(android|iphone|ipad)") { set $mobile_rewrite rewrite; } if ($uri = "/mobile/") { set $mobile_rewrite do_not_rewrite; } if ($mobile_rewrite = rewrite) { return 302 /mobile/; }希望这篇文章能帮助到你,欢迎转发、评论!
