在Web服务器管理的日常工作中,URL重写就像交通管制员手中的指挥棒。当用户请求到达服务器时,重写规则能智能地将原始请求路径转向新的目的地,同时保持浏览器地址栏不变。这种看似简单的操作背后,却解决了Web开发中的多个关键问题:
/product.php?id=123)转换为静态形式(如/product/123),提升搜索引擎收录质量/v1/api升级到/v2/api时,通过重写保持旧客户端可用性Nginx的rewrite模块通过PCRE正则表达式引擎实现这些功能,性能开销仅为Apache的mod_rewrite的1/10。我在处理电商平台迁移时,曾用20条重写规则完成了10万级商品页面的URL体系重构,QPS(每秒查询率)仍保持在8000以上。
Nginx提供三种层级的重写指令:
nginx复制# 1. server块级重写(影响所有location)
server {
rewrite ^/old/(.*)$ /new/$1;
}
# 2. location块级重写(优先级更高)
location /products {
rewrite ^/products/(\d+)$ /item.php?id=$1;
}
# 3. if条件重写(谨慎使用)
if ($http_user_agent ~* "bot") {
rewrite ^(.*)$ /bot-page.html;
}
关键参数说明:
last:停止处理当前重写规则集,用新URI重新匹配location(最常用)break:立即停止所有重写处理redirect:302临时跳转(浏览器地址栏变化)permanent:301永久跳转(SEO权重传递)警告:if指令在Nginx中被称为"邪恶的发明",不当使用会导致性能暴跌。仅在无法用map或server/location实现时才考虑if方案。
Nginx使用PCRE正则引擎,这些模式在重写中最实用:
nginx复制# 基础匹配
rewrite ^/user/(\w+)/post/(\d+)$ /profile?name=$1&pid=$2;
# 条件否定
rewrite ^/(?!admin|login).*$ /maintain.html break; # 非admin/login路径跳转
# 查询参数处理
rewrite ^/search /results?keyword=$arg_q? last;
# 文件存在性检查
if (-f $request_filename) {
rewrite ^/(.*)$ /static/$1 break;
}
实测案例:某社交平台需要将/@username重写到用户主页,同时保留/help等固定路径。解决方案:
nginx复制location / {
rewrite ^/@(\w+)$ /user/profile?handle=$1;
rewrite ^/(help|about|contact)$ /static-pages/$1.html;
}
复杂业务场景需要规则组合:
nginx复制# 第一阶段:路径标准化
rewrite ^/v1/(.*)$ /$1; # 去除版本前缀
# 第二阶段:业务路由
location /api {
rewrite ^/api/([^/]+)/(.*)$ /$1-module/index.php?action=$2;
}
# 第三阶段:异常处理
location / {
try_files $uri $uri/ @fallback;
}
location @fallback {
rewrite ^(.*)$ /404.html last;
}
这种分层策略在微服务API网关中特别有效。某金融项目通过三级重写,将80多个后端服务统一到单一入口,延迟仅增加2ms。
map指令创建变量映射表,比多重if更高效:
nginx复制map $uri $new_uri {
default $uri;
"/old-path" "/new-path";
~^/user/(\d+) "/profile?id=$1";
}
server {
rewrite ^ $new_uri;
}
实测对比:处理1000次请求,map方案比if链快3倍,内存占用减少40%。
通过rewrite_log on;开启调试日志后,在error.log中可见:
code复制2023/08/20 14:00:00 [notice] 1234#0: *1 "^/product/(\d+)$" matches "/product/42"
2023/08/20 14:00:00 [notice] 1234#0: *1 rewritten to "/item.php?id=42"
关键优化手段:
=前缀)问题1:重写循环(500错误)
nginx复制# 错误示例
rewrite ^/path /new-path;
rewrite ^/new-path /path;
解决方案:添加条件终止
nginx复制rewrite ^/path /new-path last;
问题2:查询参数丢失
nginx复制# 错误示例
rewrite ^/search /result;
# 正确写法
rewrite ^/search /result?$query_string;
问题3:HTTPS协议丢失
nginx复制# 需要显式指定协议
rewrite ^ http://example.com/new-path permanent;
# 更佳方案
return 301 https://$host$request_uri;
原始URL:
code复制/product.php?category=electronics&id=1001&ref=promo
目标URL:
code复制/electronics/1001
实现方案:
nginx复制rewrite ^/([^/]+)/(\d+)$ /product.php?category=$1&id=$2 last;
# 反向处理(保持旧链接可用)
location = /product.php {
if ($arg_category ~* "(\w+)" && $arg_id ~* "(\d+)") {
return 301 /$1/$2;
}
}
需求:将/<tenant>/<resource>映射到后端服务
nginx复制map $host $tenant_id {
default "";
"~^(?<sub>.+)\.example\.com$" $sub;
}
server {
server_name ~^(.*)\.example\.com$;
location / {
rewrite ^/(\w+)/(.*)$ /tenants/$tenant_id/$1/$2 break;
proxy_pass http://backend;
}
}
这个方案支持无限租户扩展,曾用于某教育平台服务300+学校。
在线正则测试:
^和$锚定符,模拟Nginx完整匹配Nginx调试模块:
nginx复制location /debug {
echo_exec @test_rewrite;
}
location @test_rewrite {
rewrite ^/debug/(.*)$ /actual-path/$1;
echo "Final URI: $uri";
}
日志分析命令:
bash复制# 跟踪重写过程
tail -f /var/log/nginx/error.log | grep rewrite
# 性能分析
awk '/rewrite/{print $6}' access.log | sort | uniq -c | sort -nr
在配置复杂规则时,我习惯先用curl -I测试响应头,确认跳转类型和最终地址是否符合预期。某次迁移中,这个习惯帮我发现了规则顺序错误导致的301循环。