奋斗
努力

Nginx location 匹配规则:以 WordPress 站为例讲透

云计算

一篇写给被 WordPress 伪静态和 Nginx 规则绕晕的站长。看完你会知道:为什么首页能打开、图片能缓存、后台会跳转、部分文件会 404,而爬虫还在偷偷爬你的文章页。


一、先记住一句话:location 匹配只看“路径”,不看问号后面

很多人卡在第一步,是因为把 URL 当成一整串。Nginx 不这么看。

阿里云服务器ECS 99元1年,续费不涨价: https://www.aliyun.com/minisite/goods
腾讯云轻量云服务器: https://cloud.tencent.com/act/cps/redirect?redirect=36719
京东轻量云服务器: https://3.cn/1YX-MxJv

https://example.com/?s=wordpress 在 Nginx 眼里拆成两半:

  • path(路径):/ ← location 只匹配这个
  • query string(查询串):s=wordpress ← location 完全不看

所以「搜索页」/?s=xxx 在 location 眼里就是一次对 / 的请求。理解这一点,后面所有匹配就都顺了。


二、location 的优先级:从高到低四层

Nginx 匹配 location 有明确的优先级,不是按书写顺序从上往下找:

  1. location = /path 精确匹配 —— 最高优先级,命中即停
  2. location ^~ /path 前缀匹配且停止正则 —— 本篇配置没用到
  3. location ~ regex / ~* regex 正则匹配 —— 按出现顺序,第一个命中的生效,命中即停
  4. location /path 普通前缀匹配 —— 最长前缀优先
  5. location / 兜底

一句话记忆:精确 > 正则(按顺序先到先得)> 前缀 > 兜底 /。

location ~ [^/]\.php(/|$)
{
try_files $uri =404;
fastcgi_pass unix:/tmp/php-cgi-84.sock;
fastcgi_index index.php;
include fastcgi.conf;
include pathinfo.conf;
}
location /
{
try_files $uri $uri/ /index.php?$args;
}
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
# 禁止访问的敏感文件
location ~* (\.user.ini|\.htaccess|\.htpasswd|\.env.*|\.project|\.bashrc|\.bash_profile|\.bash_logout|\.DS_Store|\.gitignore|\.gitattributes|LICENSE|README\.md|CLAUDE\.md|CHANGELOG\.md|CHANGELOG|CONTRIBUTING\.md|TODO\.md|FAQ\.md|composer\.json|composer\.lock|package(-lock)?\.json|yarn\.lock|pnpm-lock\.yaml|\.\w+~|\.swp|\.swo|\.bak(up)?|\.old|\.tmp|\.temp|\.log|\.sql(\.gz)?|docker-compose\.yml|docker\.env|Dockerfile|\.csproj|\.sln|Cargo\.toml|Cargo\.lock|go\.mod|go\.sum|phpunit\.xml|phpunit\.xml|pom\.xml|build\.gradl|pyproject\.toml|requirements\.txt|application(-\w+)?\.(ya?ml|properties))$
{
return 404;
}
# 禁止访问的敏感目录
location ~* /(\.git|\.svn|\.bzr|\.vscode|\.claude|\.idea|\.ssh|\.github|\.npm|\.yarn|\.pnpm|\.cache|\.husky|\.turbo|\.next|\.nuxt|node_modules|runtime)/ {
return 404;
}

#一键申请SSL证书验证目录相关设置
location ~ \.well-known{
allow all;
}

#禁止在证书验证目录放入敏感文件
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
return 403;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log /dev/null;
access_log /dev/null;
}
location ~ .*\.(js|css)?$
{
expires 12h;
error_log /dev/null;
access_log /dev/null;
}

我们这份 WordPress 配置里,我把include文件引入的内容直接也贴出来了,include 进来的 PHP 正则在最顶部,所以它是所有正则里第一个被尝试的——这个位置很关键,后面会反复用到。


三、把这份配置的 location 按“实际优先级”重排

配置书写顺序不等于生效顺序。按 Nginx 规则重排后是这样:

实际优先级 location 作用
1(精确) = /xmlrpc.php 直接 403,挡 XML-RPC 攻击
1(精确) = /tongji/gif.gif 内部 rewrite 到 gif.php(若有)
2(正则,第一个) `~ [^/].php(/ $)`
3 ~* 敏感文件 备份/SQL/配置等返回 404
4 ~* 敏感目录 .git/node_modules 等返回 404
5 ~ .well-known SSL 证书验证放行
6 ~ .*\.(gif|jpg|...) 图片静态缓存 30 天
7 ~ .*\.(js|css)?$ JS/CSS 静态缓存 12 小时
8(兜底) location / try_files → /index.php

注意两件事:

  • rewrite /wp-admin$ ... permanent; 写在 server 块里,不属于 location,它在 location 匹配之前、server 级 rewrite 阶段就把 /wp-admin 301 跳转了。
  • PHP 正则因为来自最顶部的,抢在所有其它正则前面接管 .php 请求。

四、逐个页面走一遍:WordPress 常见请求到底命中谁

1. 首页 /

  • 不是精确匹配,不含 .php,不命中任何敏感/静态正则
  • 落到 location /:try_files $uri $uri/ /index.php?$args
  • / 是目录,$uri/ 命中 → 触发目录 index → index.php
  • 进入 PHP 正则 → PHP-FPM → WordPress 输出首页

2. 文章页 /hello-world/、分类页 /category/news/、标签页、作者页

这是 WordPress 伪静态的核心场景。

  • 磁盘上没有hello-world 这个文件/目录
  • location / 里 try_files $uri $uri/ /index.php?$args:前两个都找不到 → 内部改写成 /index.php?$args
  • 改写后 Nginx 重新做一次 location 匹配 → 这次命中最顶部的 PHP 正则 → PHP-FPM
  • WordPress 解析 URI → 返回对应文章

划重点:伪静态不是“把 URL 变文件”,而是“找不到文件就交给 index.php 路由”。 所有文章/分类/标签/作者/日期归档,全靠这一条 try_files 兜底。

3. 搜索页 /?s=wordpress

  • path 是 /,query 是 s=wordpress,location 不看 query
  • 所以它和首页一样走 location / → try_files → index.php
  • $args 把 s=wordpress 原样带给 PHP,WordPress 才拿到搜索词

顺带戳破一个常见误解:很多人以为配置里写了含 search 的限速正则就能限住搜索页。标准 ?s= 搜索的 path 里根本没有 search 字样,限不到。 只有伪静态成 /search/xxx/ 的搜索才会命中。

4. 后台 /wp-admin(不带斜杠)

  • 在 location 匹配之前,server 块的 rewrite /wp-admin$ ... permanent 先触发
  • 301 永久跳转到 /wp-admin/
  • 浏览器地址栏变成带斜杠,后台相对资源才能正确加载

带斜杠的 /wp-admin/ 再走 location / → try_files → index.php → PHP 处理后台入口。

5. 图片 /wp-content/uploads/2024/01/photo.jpg

  • 依次试正则,PHP 正则不匹配(不是 .php)
  • 命中 ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ → 命中即停
  • Nginx 直接返回文件,expires 30d 浏览器缓存 30 天
  • 不会走 location /,也不会惊动 PHP

这里有个细节:图片 location 没有 try_files。所以图片存在就正常返回;图片不存在,Nginx 直接给自己的 404,不会回落到 WordPress 的 404 页面。这是正常的——静态资源本就该由 Nginx 管。

6. CSS / JS /wp-content/themes/x/style.css

  • 不命中 PHP、不命中图片
  • 命中 ~ .*\.(js|css)?$ → 静态返回,缓存 12 小时
  • 同样不走 location /

7. 真实存在的 PHP /index.php、/wp-admin/admin-ajax.php

  • 最顶部的 PHP 正则最先命中(它是第一个正则)
  • 进 PHP location 后先 try_files $uri =404:文件存在 → 通过
  • fastcgi_pass 交给 PHP-FPM 执行

8. 不存在的 PHP /evil.php(扫描器探测)

  • 命中 PHP 正则
  • try_files $uri =404:磁盘上没有 evil.php → 直接 404
  • 不会被丢给 PHP-FPM 执行

这条 =404 是安全护栏,防止“任意 .php 都送去 FPM”导致的信息泄露。

9. 敏感文件 /.env、/backup.sql、/composer.json

  • 命中 ~* 敏感文件 正则 → 返回 404(伪装成不存在,不泄露“这里有东西”)

10. 敏感目录 /.git/config、/node_modules/x.js

  • 命中 ~* 敏感目录 正则 → 返回 404

11. SSL 证书验证 /.well-known/acme-challenge/xxx

  • 命中 ~ .well-known → allow all
  • 但若该目录里被塞了 .php/.js/.zip 等敏感后缀,后面的 if 判断 → 403

五、一张图看懂匹配流程

请求进来
  │
  ├─ server 级 rewrite:/wp-admin$ → 301 /wp-admin/
  │
  ├─ 精确匹配 = ? ──是──> 403 或内部 rewrite(xmlrpc、tongji)
  │
  ├─ 第一个正则:~ [^/]\.php(/|$) ? ──是──> try_files=404 → PHP-FPM
  │
  ├─ ~* 敏感文件 ? ──是──> 404
  ├─ ~* 敏感目录 ? ──是──> 404
  ├─ ~ .well-known ? ──是──> 放行(敏感后缀 403)
  ├─ ~ 图片后缀 ? ──是──> 静态 30d 缓存
  ├─ ~ js|css ? ──是──> 静态 12h 缓存
  │
  └─ location / ──> try_files $uri $uri/ /index.php?$args
                        │
                        ├─ 文件/目录存在 ──> 静态或 index
                        └─ 不存在 ──> 内部改写 /index.php
                                        └─ 重新匹配 → 命中 PHP 正则 → FPM

六、这份配置里的几个“坑”,知道了能少踩

坑 1:.js|css 正则的 ?

~ .*\.(js|css)?$ 里的 ? 让分组变成“可选”,实际语义被放大,可能把某些含点号的伪静态页面误判成静态资源。规范写法是 ~ \.*\.(js|css)$。

坑 2:含 search 的限速正则拦不到 ?s=

前面说过,query 不参与 location 匹配。想限速搜索/翻页/爬取,更靠谱的是把 limit_req 直接写进 location /,覆盖所有动态页面,而不是靠一条只匹配 path 子串的正则。

坑 3:静态资源 location 没有 try_files

图片/CSS/JS 不存在时是 Nginx 自己的 404,不会走 WordPress 主题的 404 页。排查“图片 404 怎么不显示主题 404”时要知道这点。

坑 4:PHP 正则在最顶部,抢在所有正则前

include enable-php-84.conf 放在文件开头,意味着 .php 请求优先被 PHP 正则接管。只有 location = 精确匹配能压过它(比如 xmlrpc.php 的 403)。


七、回到这个问题:怎么限速防爬虫

把上面的匹配逻辑串起来,结论很清楚:

  • 静态资源(图片/CSS/JS)走各自正则,根本不经过 location /,所以写在 location / 的限速天然管不到静态资源——你不用担心采集器抓图片被限速,也不用为静态资源单独操心。
  • 所有动态页面(首页/文章/分类/搜索/分页/归档)都通过 try_files 落到 index.php,它们才是爬虫走的重灾区。
  • 所以想限速“访问 WordPress 页面的 IP”,最贴合需求的做法就是把 limit_req 写进 location /,一条覆盖全部动态页面,再配合 map 给 /wp-admin/、wp-login.php 置空 key 放行后台即可。

八、三句话记住 location

  1. location 只看 path,不看 ? 后面的 query。
  2. 优先级:精确 = > 正则 ~(按顺序先到先得)> 前缀 > 兜底 /;正则一旦命中就停止。
  3. WordPress 伪静态的本质:磁盘找不到文件 → try_files 兜底到 /index.php → 重新匹配命中 PHP 正则 → 交给 WordPress 路由。

搞懂这三条,再复杂的 Nginx 配置你也能一层层拆开看。

 

轻量服务器虽好,但是这几个隐藏的核心短板很多人还不清楚~
域名价格越来越贵,算完这笔账我沉默了:买一个com域名,够买一台2核2G的服务器了
为什么懂行的人都在抢这台188元的 2核4G5M  服务器?真相在这里
都是2核2G服务器,凭什么有的38,有的99?到底如何选择?
这台99元1年,续费不涨价的服务器,被很多人玩出了新花样~

未经允许不得转载:云服务器 » Nginx location 匹配规则:以 WordPress 站为例讲透