核心是用非正则匹配(location =、^~)替代高频路径的正则匹配,禁用贪婪回溯、加锚点限字符集,用map预筛变量和路由,精简正则顺序并主动终止扫描。

核心思路是让高频请求绕过正则引擎——Nginx 的 location = 和 location /prefix/ 是 O(1) 查表,而任何 ~ 或 ~* 都要走 PCRE 回溯,开销高一个数量级,还可能被 ReDoS 利用。
把高频路径全换成非正则匹配
从 access.log 统计前 20 名 URI,直接改写为精确或前缀形式:
-
location = /health { return 200 "ok"; }—— 比location ~ /health快且安全 -
location ^~ /static/ { root /var/www/static; expires 1y; }—— 匹配即终止,跳过所有后续正则扫描 -
location /api/v2/ { try_files $uri @api_v2_router; }—— 用前缀承接大流量,子逻辑交给内部map或命名 location 处理
重写正则本身:禁贪婪、加锚点、限字符集
避免引擎反复试探和回溯,尤其不能出现在高并发路径上:
- ❌ 错误写法:
location ~ \.(js|css|png)$—— 点号未转义,a-js-b也会匹配 - ✅ 正确写法:
location ~ \.(js|css|png|jpg|gif)$—— 显式转义点,并限定后缀范围 - 避免
.*,改用/[^/]+\.js$;所有正则必须以^开头、$结尾 - 对用户输入参与的匹配(如
$arg_id),先用map做长度或格式初筛,再进正则
用 map 预映射替代运行时拼接
当路由依赖变量动态决策时,不要在 proxy_pass 或 rewrite 中实时拼接:
- ❌ 危险低效:
proxy_pass http://$host-$env.app.local;—— 每次都要解析变量+查 hash - ✅ 安全高效:
map $host $upstream { example.com backend_v1; api.example.com backend_v2; } proxy_pass http://$upstream; - 同理,可用
map把版本路径映射为 upstream 名称,把正则逻辑从 location 层下沉到变量计算层
精简顺序 + 主动阻断正则遍历
Nginx 对正则 location 是顺序扫描,命中即停。因此:
- 把命中率最高的正则规则放在最前面(比如
.js规则比.swf更靠前) - 合并同类项:
location ~* \.(js|css|png|jpg|gif)$替代多个独立正则块 - 删除无意义通配:
location ~ /.*或location ~ .*必须清除 - 对强约定前缀(如
/admin/、/api/internal/),一律加^~,防止被后面正则误捕获,也提前终止扫描











