
本文详解 nginx 中 rewrite 指令在子路径重定向中的常见误区,重点区分 `last`(内部重写)与 `permanent`(301 外部重定向)的行为差异,并提供可落地的配置方案与调试建议。
在 Web 服务迁移或 API 路径重构过程中,常需将旧 URL 路径(如 /dataview/unknownvvvo)映射到新路径(如 /backend/unknown-vvvo)。nginx 的 rewrite 指令是实现该需求的核心工具,但其行为高度依赖末尾标志(flag),错误选择会导致 404 等静默失败——正如问题中所示:尽管日志显示 rewrite 成功,但最终返回 404。
根本原因在于 last 与 permanent 的语义差异:
-
last:内部重写(internal rewrite),仅修改当前请求的 URI,不改变浏览器地址栏,且重写后会重新匹配 location 块。在您的配置中,/dataview/unknownvvvo → /backend/unknown-vvvo 后,Nginx 会再次查找匹配 /backend/… 的 location;而您未定义该路径的处理逻辑(如 location /backend/),因此 fallback 到 location /,执行 try_files $uri /index.php… —— 此时 $uri 已为 /backend/unknown-vvvo,静态文件不存在,于是转发至 /index.php。但 PHP 应用仍接收到原始 REQUEST_URI=/dataview/unknownvvvo(因 FastCGI 参数未更新),导致路由解析失败,最终 404。
-
permanent(或 redirect):外部重定向(http 301/302),向客户端返回重定向响应,浏览器地址栏变更,发起全新请求。新请求的 REQUEST_URI 即为 /backend/unknown-vvvo,可被后端正确识别。
✅ 正确配置(推荐用于路径语义变更场景):
http { include mime.types; default_type application/octet-stream; rewrite_log on; charset utf-8; server { listen 80; listen [::]:80; root /app/app/public; index index.php index.html; # 使用 permanent 实现语义清晰的外部重定向 rewrite ^/dataview/unknownvvvo(/.*)?$ /backend/unknown-vvvo$1 permanent; rewrite ^/dataview/servicecounter(/.*)?$ /backend/servicecounter-events$1 permanent; location / { try_files $uri $uri/ /index.php?$args; } location ~ .php$ { fastcgi_pass php-fpm_cms:9000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } } }
? 关键优化说明:
- 正则中 (/.*)? 更健壮地匹配带尾斜杠或查询参数的路径(如 /dataview/unknownvvvo/ 或 /dataview/unknownvvvo?id=1);
- 将 location /index.php 改为更通用的 location ~ .php$,避免路径硬编码;
- try_files 末尾添加 $args,确保查询参数透传至 PHP;
- rewrite_log on 配合 error_log … debug 可用于调试(生产环境请关闭 debug 日志)。
⚠️ 注意事项:
- 若必须使用内部重写(如 seo 敏感、需隐藏真实路径),应配合 rewrite … last + 显式 location /backend/ 块,并在其中配置对应的后端处理逻辑;
- permanent 发起的是 301 重定向,浏览器和 CDN 会缓存,测试阶段建议先用 redirect(302);
- 所有 rewrite 规则应置于 server 块顶层(非 location 内),否则可能因作用域失效;
- 修改后务必执行 nginx -t 校验语法,并 nginx -s reload 生效。
总结:Nginx rewrite 不是“万能跳转”,而是精确控制请求生命周期的底层机制。明确业务目标——是告知用户/爬虫路径已永久迁移(用 permanent),还是服务端透明代理(需完整配套 location 与后端支持)——是写出可靠配置的第一步。