不能在同一个location块混用try_files和rewrite;应分离职责:rewrite预处理路径(如/^~/shop/中rewrite ^/shop/(.*)$ /$1 break),try_files判别资源并兜底(如location /中try_files $uri $uri/ @backend),@backend代理动态请求,静态和API需独立location拦截。

不能在同一个 location 块里混用 try_files 和 rewrite 做主路由逻辑——这是多数配置出错的根源。真正可靠的复杂路由,靠的是职责分离:用 rewrite 做路径预处理(剥离前缀、标准化格式),用 try_files 做资源判别与兜底(查文件、查目录、回退入口)。两者各走各的阶段,不交叉、不干扰。
先用 rewrite 剥离子路径并提取参数
当应用部署在 /shop/ 下,要将 /shop/product/123 转为 /product/123 传给后端,必须用 rewrite 捕获:
- 在独立的
location ^~ /shop/块中写:rewrite ^/shop/(.*)$ /$1 break; -
break是关键:它让重写后的 URI 不再触发新一轮 location 匹配,避免循环或误跳 - 此时请求已变成干净路径(如
/product/123),后续交给其他 location 处理
再用 try_files 判定静态资源并 fallback
剥离前缀后,需要区分请求是静态文件、目录,还是该进前端/后端。这时启用 try_files:
- 对根路径(
location /)配置:try_files $uri $uri/ @backend; -
$uri匹配真实文件(/static/app.js直接返回) -
$uri/匹配目录(/admin/自动找index.html) - 全失败就跳转到
@backend命名 location,交由代理或 PHP 处理
命名 location 封装动态逻辑
@backend 是纯跳转目标,不参与正则匹配,适合收口所有动态请求:
- 定义为:
location @backend { proxy_pass http://php-fpm; } - 它能接收前面
try_files传来的干净 URI(如/product/123),直接转发,无需二次重写 - 若需加参数(如
?route=),可在proxy_pass后拼接:proxy_pass http://php-fpm/index.php?route=$uri;
静态资源和 API 必须提前拦截
防止它们被 try_files 错误兜底到 HTML 或 404:
- 静态文件用精准匹配:
location ~* \.(js|css|png|woff2|ico)$ { expires 1y; add_header Cache-Control "public, immutable"; } - API 接口独立声明:
location /api/ { proxy_pass http://api-server/; }(注意末尾斜杠) - 这些 location 优先级高于
location /,Nginx 会先命中它们,完全绕过try_files



















