Nginx CPU飙升需分四步排查:先用top确认是否worker进程真占CPU;再用strace/gdb/perf定位正则回溯;接着从access.log和regex101分析触发URI与规则;最后替换嵌套量词、模糊锚定、动态拼接三类高危正则,优先改用map实现O(1)匹配。

当 Nginx 的 CPU 负载突然飙升,不能只盯着“流量大了”或“服务器卡了”这种表象。真正要做的,是快速确认:到底是 Nginx 自己在疯狂计算,还是被上游拖累、被攻击冲击、或配置埋了雷?关键在于用对工具、看准信号、分清层次。
第一步:确认是不是 Nginx worker 进程真正在吃 CPU
先排除干扰项——不是所有高 CPU 都是 Nginx 的锅。
- 运行 top -c,按 P 排序,重点看进程名是否为 nginx: worker process
- 如果高占用的是 java、php-fpm 或 node,说明问题在后端,Nginx 只是“替罪羊”
- 如果多个 worker 进程同时占满单核(比如一个 worker 持续 95%+),且 %Cpu(s) 中 us(用户态)占比高,就该深入 Nginx 内部了
第二步:抓现场,看它到底卡在哪一行代码上
Nginx worker 卡住最典型的特征,就是反复执行正则匹配——尤其是 PCRE 回溯失控。这时它不会报错,但会死循环消耗 CPU。
- 用 strace -p <worker_pid> -e trace=regexec,regcomp:如果持续看到
regexec调用,基本坐实是正则在拖慢 - 用 gdb attach <worker_pid> 后执行 bt:栈顶反复出现
pcre_exec、match或ngx_regex_exec,就是正则回溯的铁证 - 用 perf top -p <worker_pid>:若
libpcre.so或libpcre2.so占比超 40%,说明正则已成 CPU 主导消耗源
第三步:定位触发回溯的具体请求和规则
ReDoS(正则拒绝服务)从来不是均匀慢,而是某几个特定 URI 一输入就爆炸。
- 从 access.log 中筛选耗时长的请求:awk '$9 > 1 {print $7,$9}' /var/log/nginx/access.log | sort -k2nr | head -20
- 重点关注含多个点(
....)、斜杠嵌套深、路径极长、或带重复符号(如a{100})的 URI - 把可疑 URI 和对应 location/if 中的正则(例如
~ ^/api/v[0-9]+/.*/.*$)一起粘贴到 regex101.com(选 PCRE2 引擎),观察“步数”是否超万级——步数爆炸即存在灾难性回溯
第四步:识别并替换三类高危正则写法
不用通读全部配置,盯住这三种结构就能快速止损:
-
嵌套量词:如
(a+)+、(.*a){3,}、^/v\d+/(.+?)/(.+?)$ -
模糊锚定 + 贪婪匹配:如
^/static/.*\.(js|css|png)$(URI 含多个点易回溯)、location ~ \.php$(没加^和$,会全路径扫描) -
动态拼接未校验:如
if ($arg_id ~ "^$arg_type.*$") { },变量未经清洗就进正则,既慢又危险 - 优先改用 map:把高频分支固化为查表操作,例如将
if ($request_uri ~ ^/v(1|2|3)/api/.*)改为map $uri $api_version { ~^/v1/api/ 1; ~^/v2/api/ 2; default 0; },实现 O(1) 匹配,彻底绕过 PCRE



















