🔀🔀 Nginx Rewrite深度解析
📌📌 一、Rewrite核心价值与原理
✅ 核心作用:
- 🔧 URL美化:将复杂动态URL转为静态友好地址
- 🔀 路径校正:自动补全缺失斜杠或修正大小写
- 🚚 流量调度:根据条件路由到不同后端服务
- 🔒 安全加固:隐藏敏感路径参数
- 📶 访问控制:基于规则拒绝恶意请求
⚙⚙⚙️ 二、Rewrite配置语法
基础指令结构:
rewrite regex replacement [flag];
核心参数解析:
参数 | 必需 | 描述 |
---|---|---|
regex |
✅ | Perl兼容正则表达式 |
replacement |
✅ | 替换后的目标字符串 |
flag |
❌ | 控制重写行为(详见下表) |
标志位详解:
Flag | 作用 | 执行特点 |
---|---|---|
last |
终止当前location重写 | 用新URI重新搜索location |
break |
终止所有重写 | 在当前location继续执行 |
redirect |
302临时重定向 | 浏览器地址栏变化 |
permanent |
301永久重定向 | 浏览器缓存重定向 |
🔧🔧🔧 三、核心指令详解
1️⃣ rewrite
指令
location /blog/ {
# 将 /blog/123 → /posts?id=123
rewrite ^/blog/(\d+)$ /posts?id=$1 last;
}
2️⃣ if
条件判断
# 语法:if (condition) { ... }
location / {
# 移动设备重定向
if ($http_user_agent ~* "mobile|android") {
rewrite ^(.*)$ /mobile$1 last;
}
# 非法IP拦截
if ($remote_addr = "202.96.134.33") {
return 403;
}
}
条件运算符:
=
: 字符串相等!=
: 字符串不等~
: 正则匹配(区分大小写)~*
: 正则匹配(不区分大小写)-f
: 文件存在-d
: 目录存在
3️⃣ set
变量设置
location / {
set $site_version "v2";
# 根据时间设置版本
if ($time_hour > 18) {
set $site_version "nightly";
}
rewrite ^/(.*)$ /$site_version/$1;
}
4️⃣ break
与return
控制流
location /api/ {
# 终止重写链
rewrite ^/api/v1/(.*)$ /legacy/$1 break;
rewrite ^/api/v2/(.*)$ /new/$1 break;
# 直接返回响应
return 200 "API Endpoint";
}
🔄🔄🔄 四、实战配置案例
🌐 案例1:域名标准化
server {
listen 80;
server_name example.com www.example.com;
# 统一主域名
if ($host != 'example.com') {
rewrite ^(.*)$ https://example.com$1 permanent;
}
# HTTP转HTTPS
if ($scheme = http) {
rewrite ^(.*)$ https://$host$1 permanent;
}
}
📁 案例2:路径重写
location /ecommerce {
# 智能路径重写
rewrite ^/ecommerce/product-(\d+)$ /products/$1 last;
rewrite ^/ecommerce/category-(.*)$ /categories/$1 last;
rewrite ^/ecommerce/(.*)$ /shop/$1 last;
}
🛡 案例3:防盗链实现
location ~* \.(jpg|png|gif)$ {
# 允许空Referer和自身域名
valid_referers none blocked server_names *.example.com;
# 非法引用重写到水印图
if ($invalid_referer) {
rewrite ^(.*)$ /watermark$1 break;
}
}
🔀 案例4:多级路径合并
location /archive {
# /archive/2023/05/01 → /posts?date=2023-05-01
rewrite ^/archive/(\d+)/(\d+)/(\d+)$ /posts?date=$1-$2-$3 last;
# /archive/cat/tech → /category?name=tech
rewrite ^/archive/cat/(\w+)$ /category?name=$1 last;
}
📊📊 五、全局变量应用
常用内置变量:
变量 | 描述 | 应用场景 |
---|---|---|
$args |
请求参数 | 保留原始参数 |
$request_uri |
完整原始URI | 带参数的完整重定向 |
$scheme |
协议类型 | HTTP/HTTPS转换 |
$http_user_agent |
浏览器UA | 设备适配 |
$http_referer |
来源页面 | 防盗链检测 |
$remote_addr |
客户端IP | 访问控制 |
高级变量应用:
location /analytics {
# 带参数重定向:/analytics?page=home → /stats/home
if ($args ~* "page=(.*)") {
set $page $1;
rewrite ^ /stats/$page? last;
}
# 保留原始参数:/search?q=nginx → /v2/search?q=nginx
rewrite ^/search(.*)$ /v2/search$1?$args last;
}
⚠⚠⚠️ 六、高阶技巧与陷阱规避
🔧 技巧1:递归重写控制
location /download {
# 最多递归10次
rewrite_by_depth 10;
# 文件版本清理
rewrite "^/(.*)-v\d+\.(.*)$" /$1.$2 last;
}
🎭 技巧2:动态重写映射
# 创建重写映射表
map $uri $new_uri {
default "";
~^/old-blog/(.*) /new-blog/$1;
~^/shop/(.*) /ecommerce/$1;
}
server {
location / {
if ($new_uri) {
rewrite ^ $new_uri last;
}
}
}
📶 技巧3:AB测试路由
split_clients $request_uri $variant {
50% "groupA";
50% "groupB";
}
location / {
rewrite ^/(.*)$ /$variant/$1 last;
}
❌ 常见陷阱解决方案:
- 循环重定向问题
# 添加终止条件
location / {
if ($request_uri ~ "^/(.*)/$") {
set $has_slash on;
}
if ($has_slash != on) {
rewrite ^(.*)$ $1/ permanent;
}
}
- 正则性能优化
# 优化前(低效)
rewrite ^/([0-9]{4})/([0-9]{2})/([0-9]{2})/(.*)$ /archive/$1$2$3/$4;
# 优化后(高效)
rewrite ^/(\d{4})/(\d{2})/(\d{2})/(.+)$ /archive/$1$2$3/$4 last;
- 变量未定义错误
# 安全访问变量
if ($arg_id = "") {
set $arg_id "default";
}
rewrite ^/(.*)$ /item/$arg_id last;
📊📊 七、调试与监控方案
专用日志格式:
log_format rewrite_log '$remote_addr - $request_uri '
'-> $uri [$status] '
'"$http_user_agent"';
server {
rewrite_log on; # 启用重写日志
error_log /var/log/nginx/rewrite.log notice;
location / {
access_log /var/log/nginx/rewrite_access.log rewrite_log;
}
}
诊断命令集:
# 实时跟踪重写流程
tail -f /var/log/nginx/rewrite.log
# 测试重写规则
curl -I http://example.com/test-path
# 重写规则语法检查
nginx -t
性能监控:
# 统计重写次数
grep -c "rewritten" /var/log/nginx/rewrite.log
# 查找最耗时的重写
awk '/rewrite_log/ {print $NF,$7}' access.log | sort -nr