社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  NGINX

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

云知识CLOUD • 12 小时前 • 17 次点击  

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


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

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

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-knownSSL 证书验证放行
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年,续费不涨价的服务器,被很多人玩出了新花样~

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/202889