nginx rewrite需结合正则捕获、flag、if及return/set等指令协同实现复杂重写;$1等捕获变量仅在当前上下文有效;last触发重新匹配location,break终止当前块,redirect/permanent用于跳转;慎用if,避免循环与变量作用域错误。

nginx 的 rewrite 指令功能强大,但要实现真正“复杂”的 URL 重写,不能只靠单条 rewrite,而需结合正则捕获、标志位(flags)、条件判断(if)、以及与其他指令(如 return、set)协同使用。核心在于理解匹配逻辑、捕获变量作用域和执行顺序。
用正则捕获 + 变量重用构造动态路径
rewrite 支持 PCRE 正则,括号可捕获子表达式,通过 $1、$2 等引用。注意:这些变量仅在当前 rewrite 所在上下文(server 或 location 块)中有效,且仅对匹配成功的请求可用。
- 将
/article/123/title-slug重写为/index.php?id=123:rewrite ^/article/(\d+)/.+$ /index.php?id=$1 last; - 保留原始查询参数(如 ?ref=abc),需显式拼接:
rewrite ^/old/(.+)$ /new/$1?$args last;($args是完整原始 query string)
合理使用 flag 控制重写行为
flag 决定 rewrite 后如何继续处理请求,选错会导致循环重定向或规则失效。
-
last:停止当前 location 中所有 rewrite,用新 URI 重新匹配 location;适合内部路径改写 -
break:停止当前块内所有 rewrite,不重新匹配 location;适合简单路径截断 -
redirect:返回 302 临时跳转(显式加http://或https://) -
permanent:返回 301 永久跳转
例如强制 HTTPS 下的规范域名跳转:
if ($host != 'example.com') {<br>
return 301 https://example.com$request_uri;<br>
}
(注意:此处用
return 比 rewrite ... permanent 更清晰可控)配合 if 判断实现多条件路由
nginx 中 if 虽有性能和语义限制,但在 rewrite 场景下仍是必要工具。它支持对变量(如 $arg_xxx、$http_user_agent、$scheme)做简单判断。
- 对移动端用户重写到 m.example.com:
if ($http_user_agent ~* "(android|ios|mobile)") {<br> rewrite ^(.*)$ https://m.example.com$1 permanent;<br> } - 根据查询参数启用 A/B 测试路径:
if ($args ~* "test=v2") {<br> set $ab_version "v2";<br> rewrite ^/(.*)$ /ab/$ab_version/$1 break;<br> }
⚠️ 注意:if 块内不能嵌套 rewrite,且 set 定义的变量在 rewrite 中可用,但不能用于后续 if 判断(因 if 执行顺序特殊)。
避免常见陷阱
复杂 rewrite 最易出问题的地方不在语法,而在 nginx 请求处理阶段和变量生命周期。
-
location 匹配优先于 rewrite:rewrite 不改变 location 匹配结果,只改 URI;若想让重写后进入另一个 location,必须用
last触发重新匹配 - $uri 和 $request_uri 区别:$uri 是解码后的路径(不含 query),$request_uri 是原始未解码全 URI;rewrite 中通常操作 $uri 对应部分
- 循环重写检测**:nginx 默认检查 10 次重写,超限报 500;用
rewrite_log on+error_log ... debug查看每步执行过程 - 不要在 if 中使用复杂正则或多个条件组合:nginx if 不支持 && ||,多条件请拆成嵌套 if 或改用 map 指令预计算











