Nginx rewrite 地址重写
rewrite 用于改写请求 URI:可实现 301/302 跳转、伪静态、域名迁移与 URL 规范化。它与 return 指令是"改地址"的两种主要手段,本章讲清语法、四个标志与典型场景。
rewrite 语法
rewrite 只能写在 server、location、if 块中,格式为"正则 + 替换串 + 标志":
rewrite regex replacement [flag];
server {
listen 80;
server_name example.com;
rewrite ^/old/(.*)$ /new/$1 last; # /old/a.html → /new/a.html
}
替换串中的 $1、$2 引用正则里的捕获组,$host、$request_uri 等内置变量也可使用。
四个标志的含义
| 标志 | 行为 | 典型用途 |
|---|---|---|
| last | 停止当前 rewrite,用新 URI 重新匹配 location | 改写后想进入另一个 location |
| break | 停止 rewrite,直接用新 URI(不再重匹配) | 改写后仍留在当前块处理 |
| redirect | 临时重定向,返回 302 | 临时迁移、临时维护页 |
| permanent | 永久重定向,返回 301 | 换域名、URL 永久改变 |
location /download/ {
rewrite ^/download/(.*)$ /files/$1 permanent; # 外链随 301 转移到新地址
}
last 与 break 的区别
两者都会停止本次 rewrite 处理,区别在后续行为:last 用新 URI 重新走一遍 location 匹配(可能进入别的 location),break 则停留在当前 location 继续执行其内部指令:
location /api/ {
rewrite ^/api/(.*)$ /v2/$1 break; # 改写后仍在 /api/ 块内
proxy_pass http://127.0.0.1:8080; # 实际转发的新 URI 是 /v2/xxx
}
场景一:HTTP 跳 HTTPS
整站强制 https,推荐在 80 端口 server 里用 return 一次性完成(比 rewrite 简洁,也天然避免循环,见第 13 章):
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
等价写法:rewrite ^(.*)$ https://$host$1 permanent;
场景二:域名迁移
旧域名整体 301 到新域名,把搜索引擎权重平滑转移:
server {
listen 80;
server_name old-domain.com www.old-domain.com;
rewrite ^(.*)$ http://www.new-domain.com$1 permanent;
}
场景三:伪静态
把 /article/123.html 这类地址在内部映射到动态脚本,浏览器地址栏不变、无 302:
location / {
rewrite ^/article/([0-9]+)\.html$ /index.php?id=$1 last;
}
return 与 rewrite 怎么选
| 场景 | 推荐 |
|---|---|
| 只要 301/302 状态码跳转 | return code url(最直接) |
| 改写 URI 后交给其他 location 处理 | rewrite ... last |
| 命中条件后想直接返回内容/状态码 | return code text |
避免重定向循环
循环的典型成因:rewrite 把 /a 改成 /b,而 /b 又满足同一条 rewrite 条件,反复改写直至浏览器报"重定向过多"。规范做法:80 端口单独一个 server 只做 return 301,443 的 server 内部不再写协议跳转;确需条件判断时用 if ($scheme != "https") 包裹,且务必保证条件在改写后不再成立。
小结:rewrite 的核心是"正则改写 URI + 标志控制后续行为";纯跳转优先 return,内部路径改写用 last/break,跨站迁移用 permanent。拿不准是否循环时,用 curl -I 观察响应里的 Location 头即可判断。