为什么你的Nginx会无限308重定向?
今天给一个私有化项目排查故障问题表现是请求会返回无限308重定向最后定位是因为Nginx开启了强制HTTPS的重定向而未做判断。借这个机会把Nginx的无限重定向的原理和处理方法给大家做下分享。什么是 308 状态码308 Permanent Redirect 是 HTTP/1.1 协议中引入的状态码与 301 类似但有一个关键区别301/302重定向返回可能会被浏览器将原本的 POST 请求改为 GET 请求307/308严格保持请求方法和请求体不变307 临时重定向对应302308 永久重定向对应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访问CDNCDN回源到后端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 转 wwwserver { listen 443 ssl; server_name example.com; return 308 https://www.example.com$request_uri; }www 转非 wwwserver { 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/; }希望这篇文章能帮助到你欢迎转发、评论