Nginx rewrite指令基本语法为rewrite regex replacement flag;执行顺序为:先server块rewrite,再location匹配,最后执行匹配location内的rewrite,且URI重写后可能触发新一轮location匹配。

rewrite指令的基本语法和执行顺序
nginx的rewrite规则不是写完就生效,它依赖于所在作用域(server、location)和匹配优先级。规则按书写顺序逐条执行,一旦某条匹配成功且未加last或break标志,后续规则仍会继续尝试——这点和Apache的RewriteRule默认行为不同,容易导致意外重写循环。
常见写法:rewrite ^/old/(.*)$ /new/$1 permanent;,其中permanent表示301跳转,redirect表示302;不加则为内部重写(URI变更但客户端无感知)。
- 正则表达式必须用
^和$明确边界,否则可能误匹配(如/old会匹配/old-page) - 捕获组用
(...),引用用$1、$2,不能用\1 - 所有
rewrite都在URI解码后处理,所以中文或特殊字符需先确保已正确解码
location块中rewrite与内部重定向的冲突
在location ~ \.php$这类正则location里直接写rewrite,常导致PHP脚本无法执行——因为rewrite后的URI可能不再匹配原location,而nginx不会自动重新匹配新的location块,除非显式用last触发重试。
例如:location /blog { rewrite ^/blog/(.*)$ /index.php?slug=$1 last; },这里last会让nginx丢弃当前location,用新URI重新查找匹配的location(比如落到location ~ \.php$里),而break则只在当前location内重写,不重匹配。
- 用
last时,新URI会再次进入location匹配流程,适合需要切换处理逻辑的场景 - 用
break时,重写后仍在当前location内处理,适合静态路径调整 - 避免在
if块中使用rewrite(官方明确不推荐),它会导致变量作用域异常和性能下降
带查询参数的重写要显式保留
nginx默认不继承原始请求的query string,rewrite ^/a/(.*)$ /b/;会丢掉?id=123。这不是bug,是设计行为。
正确做法是手动拼接:rewrite ^/a/(.*)$ /b/$1?$args;,其中$args是原始查询参数字符串;如果想清空参数,得显式写?(结尾问号)来截断。
-
$args是只读变量,不能修改,也不能用set $args ...覆盖 - 若需增删参数,得用
map或第三方模块(如ngx_http_substitutions_filter_module),纯rewrite做不到 - 注意
?的位置:写成/b/$1?$args才对,写成/b/$1?$args&x=y会重复添加
调试rewrite规则的实用方法
最直接的方式是开启nginx的rewrite日志:rewrite_log on;配合error_log /path/to/error.log notice;,但注意该日志只在debug编译版中可用,生产环境通常关着。
更可行的是用return临时拦截并查看原始URI:location /test { return 200 "URI: $uri, args: $args"; },再结合curl验证输入输出。
- 浏览器开发者工具的Network面板看响应头
Location,确认301/302是否符合预期 - 用
curl -I http://host/old/path检查状态码和跳转目标 - 所有rewrite规则上线前,务必用
nginx -t校验语法,一个错位括号就能让整个server块失效
rewrite看似简单,但正则边界、location匹配时机、参数继承这三处最容易出问题,改一条规则最好连带检查上下游依赖路径。











