Nginx location 匹配与 rewrite:规则写错就 404
Nginx 配置里最容易出问题的就是 location 匹配。规则写错的表现是「明明文件在,就是 404」。
一、四种匹配方式
location = /exact { } # 精确匹配,优先级最高
location ^~ /static/ { } # 前缀匹配,命中后不再查正则
location ~ .php$ { } # 正则匹配(区分大小写)
location ~* .(png|jpg)$ { } # 正则匹配(忽略大小写)
location /general { } # 普通前缀匹配,优先级最低优先级顺序:
1. = 精确匹配(命中即停止)
2. ^~ 前缀匹配(命中即停止,不再看正则)
3. ~ ~* 按配置文件中出现的先后顺序,第一个命中即停止
4. 普通前缀 选「最长」的那个,然后还要继续查正则关键理解:普通前缀匹配不会立即停止,它会记住最长的那个,然后继续检查正则;如果正则命中,正则会覆盖它。而
^~和=会立即停止。
二、例子:为什么我的规则没生效
location /api {
proxy_pass http://127.0.0.1:3000;
}
location ~ ^/api/(v1|v2)/ {
proxy_pass http://127.0.0.1:4000;
}
# 请求 /api/v1/user
# → 两个都匹配,正则优先级更高 → 走 4000如果不想要这个行为,给前缀加 ^~:
location ^~ /api {
proxy_pass http://127.0.0.1:3000; # 现在 /api/v1/user 也走 3000
}三、root 与 alias(404 高发区)
| 指令 | 拼接方式 |
|---|---|
root | 完整 URI 拼到 root 后面 |
alias | 用 alias 路径替换 location 匹配的部分 |
location /static/ {
root /var/www/baize;
}
# 请求 /static/a.css → /var/www/baize/static/a.css
location /static/ {
alias /var/www/baize/assets/;
}
# 请求 /static/a.css → /var/www/baize/assets/a.css别名目录(alias)要带结尾斜杠,否则路径拼接会少一层。
常见错误:
# 错误:alias 在正则 location 里要写捕获组
location ~ ^/img/(.+\.png)$ {
alias /data/img/$1; # 正确
# alias /data/img; ← 错误
}四、rewrite 的四个 flag
rewrite ^/old/(.*)$ /new/$1 last;
rewrite ^/old/(.*)$ /new/$1 break;
rewrite ^/old/(.*)$ /new/$1 redirect; # 302
rewrite ^/old/(.*)$ /new/$1 permanent; # 301| flag | 行为 |
|---|---|
last | 重写后重新走一遍 location 匹配 |
break | 重写后停止 rewrite 阶段,在当前 location 继续处理 |
redirect | 返回 302 临时重定向给浏览器 |
permanent | 返回 301 永久重定向给浏览器 |
last和break的区别是面试常问,也是实际最容易搞混的:last会重新匹配 location(可能走到另一个 location),break不会。
死循环风险:last 如果重写后又匹配回同一个 location,会循环 10 次然后报 500。
五、常见场景
强制 HTTPS
server {
listen 80;
server_name bzii.cn www.bzii.cn;
return 301 https://www.bzii.cn$request_uri;
}用
return比rewrite更高效,也更清晰。能用 return 就别用 rewrite。
统一 www
server {
listen 443 ssl;
server_name www.bzii.cn;
return 301 https://www.bzii.cn$request_uri;
}去掉 .html 后缀
location / {
try_files $uri $uri.html $uri/ =404;
}
# 或更友好:重定向到无后缀版本
if ($request_uri ~ ^/(.*)\.html$) {
return 301 /$1;
}SPA 回退
location / {
try_files $uri $uri/ /index.html;
}旧链接迁移(多组规则)
rewrite ^/blog/(\d+)/(\d+)/(.+)$ /posts/$3 permanent;
rewrite ^/tag/(.*)$ /tags/$1 permanent;
rewrite ^/feed$ /feed.xml permanent;六、try_files 是更现代的选择
location / {
try_files $uri $uri/ $uri.html /index.html =404;
}
location /images/ {
try_files $uri /images/default.png;
}try_files 按顺序尝试,都不存在就用最后一个(可以是命名 location 或状态码)。比 rewrite 直观。
七、404 排查流程
# 1. 看是哪个 location 在处理(加日志)
log_format dbg '$request_uri → $uri → $document_root → $status';
# 2. 确认文件真的存在且可读
ls -l /var/www/baize/static/a.css
sudo -u www-data test -r /var/www/baize/static/a.css && echo OK
# 3. 看错误日志(会写出实际尝试的路径)
tail -5 /var/log/nginx/error.log
# 典型:open() "/var/www/baize/static/a.css" failed (2: No such file or directory)
# 4. 确认配置生效
sudo nginx -T | grep -A5 "location"错误日志里的路径是最直接的线索:看它实际去哪找文件了,立刻就知道 root/alias 配错在哪。
八、403 的常见原因
| 原因 | 检查 |
|---|---|
| 目录没有 x 权限 | namei -l /var/www/baize/x |
| 文件没有 r 权限 | ls -l |
| autoindex 关闭且无 index 文件 | 加 index 或开 autoindex |
| 被 deny 规则挡住 | 检查 deny/allow |
九、检查清单
- location 优先级符合预期(正则会覆盖普通前缀)
- root 与 alias 用对了(斜杠是否匹配)
- rewrite 用了正确的 flag
- 没有 rewrite 死循环
nginx -t通过- 改完
nginx -s reload
location 的规则不多,但优先级和 root/alias 这两处几乎占了所有「莫名其妙 404」的成因。
