伪静态重写必须在proxy_pass前由Nginx完成,使用rewrite+last触发重新匹配,确保后端接收到/index.php?xxx格式URI;PHP需显式设置SCRIPT_NAME避免路由解析错误。

在 Nginx 中使用 proxy_pass 转发请求时,伪静态规则(如将 /post/123 重写为 /index.php?post_id=123)**不能直接在后端应用层处理**,而必须由 Nginx 在代理前完成重写。因为一旦转发给后端(如 PHP-FPM 或 Node.js 博客服务),原始 URL 路径已丢失或被覆盖,后端无法得知用户访问的是伪静态路径。
确保重写发生在 proxy_pass 之前
Nginx 的 rewrite 指令必须放在 location 块中、proxy_pass 之前,否则不会生效。Nginx 处理顺序是:匹配 location → 执行 rewrite → 再执行 proxy_pass(使用重写后的新 URI)。
- 错误写法(rewrite 在 proxy_pass 之后,会被忽略):
location /post/ {
proxy_pass http://backend;
rewrite ^/post/(\d+)$ /index.php?post_id=$1 break; # ← 不生效!
}- 正确写法(rewrite 在前,且用
last触发重新匹配,或用break终止当前 location):
location /post/ {
rewrite ^/post/(\d+)$ /index.php?post_id=$1 last;
proxy_pass http://backend;
}
注意:last 会让 Nginx 用新 URI 重新查找匹配的 location,适合配合 PHP 处理;若后端是统一入口(如 /index.php),建议再配一个匹配 /index.php 的 location 来真正转发。
典型博客伪静态 + 反向代理完整配置示例
假设博客后端运行在 http://127.0.0.1:3000(Node.js)或 FastCGI(PHP),前端 Nginx 需支持:/post/123 → /index.php?post_id=123,同时保留其他静态资源直通、首页和分类页规则。
upstream blog_backend {
server 127.0.0.1:3000; # 或 php-fpm socket/fcgi
}
<p>server {
listen 80;
server_name blog.example.com;</p><pre class="brush:php;toolbar:false;"># 静态资源不代理,直接返回
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
root /var/www/blog/public;
}
# 伪静态规则:/post/123 → /index.php?post_id=123
location ^~ /post/ {
rewrite ^/post/(\d+)$ /index.php?post_id=$1 last;
}
# 伪静态规则:/category/webdev → /index.php?category=webdev
location ^~ /category/ {
rewrite ^/category/(.+)$ /index.php?category=$1 last;
}
# 伪静态规则:/tag/nginx → /index.php?tag=nginx
location ^~ /tag/ {
rewrite ^/tag/(.+)$ /index.php?tag=$1 last;
}
# 主入口:所有未匹配的动态请求交给后端
location / {
try_files $uri $uri/ @backend;
}
# 后端代理入口(承接 rewrite 后的 /index.php?xxx)
location @backend {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://blog_backend;
}}
关键点:^~ 修饰符确保路径前缀匹配优先于正则 location;last 让重写后的 URI 进入新的 location 匹配流程(最终落到 @backend)。
如果后端是 PHP(FastCGI),需额外适配 SCRIPT_NAME
当使用 proxy_pass 转发到 PHP-FPM(如 via fastcgi_pass)时,重写后的 /index.php?post_id=123 必须让 PHP 知道真实脚本路径,否则 $_SERVER['SCRIPT_NAME'] 可能出错。
- 在 fastcgi location 中显式设置:
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME /index.php; # 强制设为 /index.php,避免被原始 URI 干扰
fastcgi_pass php-fpm;
}
这样即使请求是 /post/123,经 rewrite 后走 /index.php?post_id=123,PHP 仍能正确解析路由参数。
调试技巧:确认重写是否生效
在配置中临时加入日志,验证重写行为:
- 开启 rewrite 日志(仅开发环境):
rewrite_log on; error_log /var/log/nginx/rewrite.log notice;
- 用
curl -v http://blog.example.com/post/456查看响应头中的X-Original-URI(可自定义添加)或检查后端收到的REQUEST_URI值; - 在后端代码中打印
$_SERVER['REQUEST_URI'](PHP)或req.url(Node.js),确认是否为/index.php?post_id=456。











