问题描述
对于http://info.example.com/special/hotelbrand/honghe/地址,如果使用如下配置:
if ( $uri !~ ^/special/.* ) { rewrite ^(.*)$ https://www.example.com/zx/ permanent; }
在配置中的正则表达式与地址匹配,根据if语句(不匹配则跳转),则页面不应该进行跳转。但是在实际运行过程中,页面被重定向,与预期不符。
原因分析
原因在于我们没有正确理解$request_uri变量与$uri变量。问题在于我们对很多地方的理解有误。
问题分析
随着问题深入调查,我们发现如下配置才会导致问题:
server { listen 80; server_name info.example.com; if ( $uri !~ ^/special/.* ) { rewrite ^(.*)$ https://www.example.com/zx/ permanent; } error_page 404 = /404.html; }
也许老手已经看到问题所在了…………事情是这个样子的…………
首先,我们访问的连接不存在。根据error_page 404 = /404.html;配置,我们被“内部重定向”到404.html页面。所谓“内部重定向”是返回404.html页面内容,就好像客户端端在访问404.html页面,而不是让客户端浏览器重定向404.html页面。
但是…………当他内部重定向到404.html时,遇到if语句,而if语句中的$uri变量此时为/404.html值(因为$uri就是这个样子),然后客户端被重定向到在rewrite中定义的连接。
更进一步
如果理解上面的内容,你就能够理解为什么下面的配置会跳转到https://404.example.com地址:
server { listen 80; server_name info.example.com; if ( $uri ~ /404.html ) { rewrite ^(.*)$ https://404.example.com permanent; } if ( $uri !~ ^/special/.* ) { rewrite ^(.*)$ https://www.example.com/zx/ permanent; } error_page 404 = /404.html; }
解决办法
将$uri变量替换为$request_uri变量(它是完整的原始请求URI值,不会发生变化)。
附加说明
request uri vs uri
$uri – 在请求中的当前URI,被“标准化”后的。所谓标准化是指,解码被编码的字体(以“%XX”形式),解析相对路径引用,将联系反斜线合并为壹个。在请求处理期间,变量$uri的值可能发生变化。比如进行内部重定向,或者使用索引文件。
$request_uri – 完整的原始请求URI(带有参数)
参考文献
NGINX $request_uri vs $uri
Module ngx_http_core_module/$uri
Module ngx_http_core_module/$request_uri
Module ngx_http_core_module/error_page