一、引言:URL不只是地址,更是用户体验与安全的门面
你是否遇到过这些问题:
- 网站改版后,旧链接全部失效,导致用户收藏夹和搜索引擎索引"404"?
- 想要将所有 HTTP 流量强制跳转到 HTTPS,但又不想在代码里写一堆判断?
- 希望隐藏真实的后端接口路径(如
/api/v1/user),对外暴露更简洁的 URL(如/user)? - 需要根据请求参数或客户端类型(PC/手机)返回不同的内容?
这些问题的答案,都指向同一个强大的 Nginx 功能------URL Rewrite(URL重写)。
它就像一个智能的"交通指挥官",能在请求到达后端应用之前,对 URL 进行修改、重定向或内部跳转。本文将带你从语法基础到十大经典场景,彻底掌握这项核心技能。
💡 核心价值 :
精通 URL Rewrite,你就能优雅地处理网站迁移、提升SEO、增强安全性,并构建极其灵活的路由规则!
二、核心基石:rewrite 指令详解
Nginx 的 URL 重写功能由 ngx_http_rewrite_module 模块提供,其核心是 rewrite 指令。
基本语法
rewrite <regex> <replacement> [flag];
<regex>: Perl 兼容的正则表达式 (PCRE),用于匹配请求的 URI。<replacement>: 重写后的 URI。可以使用$1,$2... 来引用正则中的捕获组。[flag]: 可选标志位,控制重写后的行为。这是最容易混淆但也最关键的部分。
四大核心标志位(Flag)深度解析
| 标志位 | 作用 | 类比 |
|---|---|---|
last |
停止当前 server 块内的 rewrite 检查,并重新搜索 location。 |
"内部重写"。浏览器地址栏不变,服务器内部用新 URI 重新匹配 location。 |
break |
停止当前 server 块内的 rewrite 检查,不再重新搜索 location。 |
"内部中断"。直接使用当前重写后的 URI 处理请求,通常用于静态文件映射。 |
redirect |
返回 302 临时重定向。 | "临时搬家"。浏览器地址栏会变成新的 URL。 |
permanent |
返回 301 永久重定向。 | "永久搬家"。浏览器和搜索引擎会更新书签和索引。 |
关键区别 :
last和break是内部操作 ,对用户不可见;redirect和permanent是外部重定向,会触发浏览器跳转。
三、十大经典应用场景实战
场景一:HTTP 强制跳转 HTTPS(SEO友好)
server {
listen 80;
server_name www.example.com;
# 永久重定向到HTTPS
return 301 https://$server_name$request_uri;
# 或者用rewrite
# rewrite ^(.*)$ https://$server_name$1 permanent;
}
最佳实践 :优先使用
return指令,因为它更高效、语义更清晰。
场景二:网站改版,旧URL平滑迁移(301重定向)
# 旧文章页: /blog/post?id=123
# 新文章页: /article/123.html
rewrite ^/blog/post$ /article/$arg_id.html? permanent;
# 更复杂的路径映射
rewrite ^/old-category/(.*)$ /new-category/$1 permanent;
注意 :
$arg_id是 Nginx 内置变量,用于获取id参数的值。
场景三:隐藏真实路径,美化URL(内部重写)
# 对外暴露 /user/123
# 内部实际请求 /api/v1/user?id=123
location /user/ {
rewrite ^/user/(\d+)$ /api/v1/user?id=$1 last;
proxy_pass http://backend;
}
用户看到的是干净的 /user/123,后端收到的是带参数的传统接口。
场景四:根据设备类型(PC/手机)返回不同内容
# 检测User-Agent
if ($http_user_agent ~* "(mobile|android|iphone|ipod)") {
rewrite ^(.*)$ $1/mobile/ last;
}
location / {
root /var/www/html;
# PC用户访问 /index.html
# 手机用户访问 /mobile/index.html
}
场景五:阻止特定爬虫或恶意请求
if ($http_user_agent ~* "bad-bot|spam-crawler") {
return 403;
}
# 阻止访问 .env, .git 等敏感文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
场景六:统一URL格式(带/或不带斜杠)
# 将 /page 重定向到 /page/ (目录形式)
rewrite ^([^.]*[^/])$ $1/ permanent;
场景七:实现多级域名到路径的映射
# user1.example.com -> example.com/user/user1
# user2.example.com -> example.com/user/user2
if ($host ~* "^([a-z0-9-]+)\.example\.com$") {
set $username $1;
rewrite ^(.*)$ /user/$username$1 last;
}
场景八:API版本控制
# /v2/api/... -> /api/v2/...
rewrite ^/v2/(.*)$ /api/v2/$1 last;
场景九:防盗链(防止图片被其他网站直接引用)
location ~* \.(jpg|jpeg|png|gif)$ {
valid_referers none blocked *.yourdomain.com yourdomain.com;
if ($invalid_referer) {
return 403;
# 或者返回一个占位图
# rewrite ^/.*$ /images/placeholder.png break;
}
}
场景十:配合动静分离,处理SPA路由
location / {
root /var/www/app;
try_files $uri $uri/ @rewrites;
}
location @rewrites {
# 所有未匹配到文件的请求,都重写到 index.html
rewrite ^(.*)$ /index.html last;
}
这是 Vue/React 等单页应用(SPA)在 history 模式下的标准配置。
四、性能与最佳实践
- 慎用
if:Nginx 官方文档有句名言:"If is Evil "。if指令在location块中行为复杂,容易出错。能用location匹配就不要用if。 - 优先
return:对于简单的重定向(如 301/302),return比rewrite更高效。 - 避免循环 :确保你的重写规则不会导致无限循环。例如,重写
/old到/new,但/new又被重写回/old。 - 测试先行:在生产环境部署前,务必在测试环境中充分验证你的规则。
五、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!