关键在于用Nginx正则捕获参数并301跳转至伪静态路径:如rewrite ^/product.php?id=(\d+).*$ /item/$1 permanent;优先用map或rewrite直接匹配URI,避免if嵌套;注意$uri、$args、$request_uri区别,测试用curl -I验证响应头。

要将带参数的旧链接(如 /product.php?id=123&ref=abc)完美映射到新版伪静态路径(如 /item/123),关键不是简单跳转,而是提取参数、构造新路径、保留必要上下文、避免丢参或循环。Nginx 本身不解析 PHP 查询参数,必须靠正则捕获 + rewrite 或 return 配合变量来实现。
用 rewrite 提取参数并生成伪静态路径
适用于旧 URL 含有可识别结构(如 ?id=数字),且新路径需基于该数字构建(如 /item/123):
- 把规则写在
location /或更精确的location ~ \.php$块中(避免影响静态资源) - 用
rewrite捕获id值,并用permanent发出 301 -
$args可显式拼接其他参数(如需保留ref),但伪静态路径通常不带查询串,建议只提关键 ID
示例配置:
server {listen 80;
server_name old.com;
location / {
# 匹配 product.php?id=123&xxx
rewrite ^/product\.php\?id=(\d+)(?:&.*)?$ /item/$1 permanent;
}
}
用 return 301 + $request_uri 处理“参数即路径”的场景
如果旧链接参数本身就是路径语义(如 /page?id=about → 新版 /about),而你又不想用 rewrite,可用 map 预定义映射关系,再配合 return:
- 先在
http块外层定义map,把常见id值映射为路径别名 - 在
location中判断$args是否含目标参数,再用return 301跳转 - 注意:不能在
if中直接用return(Nginx 官方不推荐),应改用map+try_files或error_page组合
精简安全做法(推荐):
map $args $new_path {default "";
"id=about" "/about";
"id=contact" "/contact";
"id=products" "/products";
}
server {
listen 80;
server_name old.com;
location = /page {
if ($new_path) {
return 301 $new_path;
}
return 404;
}
}
保留原始参数并兼容 SEO 的折中方案
当新版伪静态路径仍需承载部分参数(如分页、筛选),但又想隐藏 .php 和 ?,可做两步处理:
- 第一步:301 跳转时把
id=123&page=2映射为/item/123?page=2(仍带参数,但路径干净) - 第二步:后端或前端路由接管
/item/123,把page=2解析为状态,或由 Nginx 再次内部重写(非 301)到真实脚本 - 避免在跳转中用
$host或$scheme拼接,容易导致协议错乱;固定写https://new.com更稳妥
例如:
location ~ ^/product\.php$ {if ($args ~* "id=(\d+)") {
set $id $1;
rewrite ^.*$ /item/$id?$args permanent;
}
}
必须避开的坑
这类映射最容易出错的地方不在语法,而在逻辑边界:
- 不要用
if ($args ~ id=)匹配后直接rewrite,Nginx 中if在 location 内行为不可靠,优先用map或rewrite直接匹配 URI -
$request_uri含完整路径和参数,$uri不含参数,$args只含参数——三者别混用 - 测试时用
curl -I http://old.com/product.php?id=123看响应头是否为301且Location: https://new.com/item/123 - 上线前务必清浏览器和 CDN 缓存,否则 301 会被强缓存,改错也跳不回来


















