rewrite功能是Nginx服务器中一个极为强大的URL处理模块,它允许我们对传入的请求URI进行动态修改和重定向。作为一名运维工程师,我经常使用rewrite来解决各种URL处理需求,从简单的重定向到复杂的条件跳转。
rewrite模块(ngx_http_rewrite_module)的工作流程可以概括为:
这个过程中最关键的三个要素是:
提示:rewrite模块依赖于PCRE库,在编译Nginx时必须确保已安装pcre-devel等开发包,否则rewrite功能将无法使用。
在实际工作中,rewrite最常见的几种用途包括:
URL规范化:将不同格式的URL统一为标准形式
example.com/About重写为example.com/about新旧域名迁移:旧域名跳转到新域名
old.com/* → new.com/*动态URL伪装:将动态参数伪装成静态路径
product.php?id=123 → product/123.html条件访问控制:基于IP、UA等条件进行跳转
A/B测试:将部分流量导向不同版本
rewrite模块提供了几个关键指令:
nginx复制# 启用rewrite日志(调试时非常有用)
rewrite_log on;
# 关闭未初始化变量警告
uninitialized_variable_warn off;
rewrite日志会记录到error_log中,需要将error_log级别设置为notice才能查看:
bash复制error_log /var/log/nginx/error.log notice;
rewrite指令的标准格式为:
nginx复制rewrite regex replacement [flag];
各参数说明:
| flag值 | 作用 | 浏览器地址栏变化 | 请求次数 |
|---|---|---|---|
| last | 停止处理当前rewrite,用新URI重新开始location匹配 | 不变 | 1次 |
| break | 停止处理所有rewrite,继续执行后续非rewrite指令 | 不变 | 1次 |
| redirect | 返回302临时重定向 | 变化 | 2次 |
| permanent | 返回301永久重定向 | 变化 | 2次 |
rewrite的核心在于正则表达式的运用,以下是一些实用技巧:
基础匹配:
nginx复制rewrite ^/oldpath /newpath; # 匹配/oldpath开头的URI
捕获组使用:
nginx复制rewrite ^/user/([0-9]+)/profile /profile.php?id=$1;
# 将/user/123/profile 转为 /profile.php?id=123
条件判断:
nginx复制if ($http_user_agent ~* "mobile") {
rewrite ^ /mobile last;
}
文件存在性检查:
nginx复制if (!-f $request_filename) {
rewrite ^ /fallback last;
}
需求:将旧域名www.old.com迁移到www.new.com,保持所有参数不变。
nginx复制server {
listen 80;
server_name www.old.com;
location / {
rewrite ^(.*)$ http://www.new.com$1 permanent;
}
}
关键点:
permanent返回301永久重定向$1捕获原始URI路径需求:将/product.php?id=123伪装为/product/123.html
nginx复制rewrite ^/product/([0-9]+)\.html$ /product.php?id=$1 last;
优化建议:
需求:仅允许公司IP(192.168.1.100)访问,其他IP显示维护页面。
nginx复制set $allow false;
if ($remote_addr = "192.168.1.100") {
set $allow true;
}
if ($allow = false) {
rewrite ^ /maintenance.html break;
}
location = /maintenance.html {
root /var/www/error_pages;
}
需求:移动设备访问且路径为/blog时跳转到移动版。
nginx复制map $http_user_agent $is_mobile {
default 0;
"~*(android|iphone)" 1;
}
server {
...
if ($is_mobile = 1) {
rewrite ^/blog(.*)$ /mobile/blog$1 last;
}
}
开启rewrite日志:
nginx复制rewrite_log on;
error_log /var/log/nginx/error.log notice;
使用echo模块调试:
nginx复制location /test {
echo "Original URI: $uri";
rewrite ^/test/(.*)$ /new/$1;
echo "Rewritten URI: $uri";
}
分阶段测试:
问题1:rewrite规则不生效
问题2:陷入重定向循环
问题3:变量值不符合预期
nginx复制map $http_host $new_site {
default "";
"old.com" "new.com";
"blog.old.com" "blog.new.com";
}
server {
...
if ($new_site) {
rewrite ^ $scheme://$new_site$request_uri permanent;
}
}
nginx复制location / {
try_files $uri $uri/ @rewrite;
}
location @rewrite {
rewrite ^/(.*)$ /index.php?q=$1 last;
}
nginx复制set $ab_test "A";
if ($cookie_ab_test) {
set $ab_test $cookie_ab_test;
}
location /product {
if ($ab_test = "B") {
rewrite ^ /product_new last;
}
}
nginx复制rewrite ^/api/([^/]+)/(.*)$ /$2 break;
proxy_pass http://$1_upstream;
避免开放重定向:
nginx复制# 不安全的写法
rewrite ^/redirect(.*)$ $1 permanent;
# 安全的写法
rewrite ^/redirect/(https?://[^/]+/.+)$ $1 permanent;
防范正则表达式拒绝服务:
敏感路径保护:
nginx复制location ~* ^/(admin|config) {
deny all;
rewrite ^ /403.html break;
}
输入验证:
nginx复制if ($args ~* "\.\./") {
return 403;
}
在实际生产环境中使用rewrite时,我建议先在测试环境充分验证所有规则,使用curl -v命令跟踪重定向过程,逐步调试直到完全符合预期。rewrite虽然强大,但不恰当的使用可能导致循环重定向、性能下降甚至安全漏洞。