恶意机器人无法阻止,改在应用前截断——在 nginx 阶段整理奇怪 URL

nginx blackhole.conf 整理奇怪 URL

当把 Web 应用暴露到互联网时,无论使用哪种框架,奇怪的请求都会蜂拥而至。

  • 根本不存在的 /wp-admin/
  • 甚至不写 PHP 也会出现 /index.php/xmlrpc.php
  • .git.env/cgi-bin/ 等敏感文件/目录
  • wso.phpshell.phptest.php

这不是你自己写的服务,而是全球扫描器和机器人在“是否存在漏洞?”时尝试的 URL。说实话,几乎没有服务器能完全免受这些请求。

幸运的是,如果应用层的防御机制做得好,绝大多数会被 400/404 正确处理。问题在于之后的后果。

  • 日志被污染。真正用户请求之前,奇怪请求就占据了日志的一半。
  • CPU 也被消耗。虽然“安全”但无谓的运算让人不安。
  • 心情疲惫。每次查看日志看到 /wp-login.php 占据数百行,疲劳感堆积。

因此,我把这些请求完全在应用前的 nginx 层截断,让它们在主机层直接进入“黑洞”。

本文将整理:

  • 为什么在 nginx 进行一次性拦截很有用
  • 如何用 blackhole.conf 等文件统一管理拦截规则
  • 实际可用的 nginx 配置示例

1. 只在应用层拦截的不足



我们常用的基本做法是:

  • 路由/控制器不存在的 URL → 404
  • 异常处理 → 400/500
  • 日志 → APM / log collector 收集

功能上没问题,但从运维角度来看有几个痛点。

  1. 日志噪声 * APM、日志系统中的错误/404 比例被扫描器拉高。 * 每次都得人工判断“是真正用户还是扫描器”。
  2. 层级过低拦截 * 请求已进入应用,意味着已通过框架、middleware、路由层。 * 这时再想“为什么要让它到这里?”就显得多余。
  3. 细微但累计的资源消耗 * 单次无关紧要,但 24 小时持续的扫描流量会占用大量请求。

所以我倾向于在上层尽量清理这些无用请求。


2. 在 nginx 中创建黑洞:直接返回 444

nginx 有一个HTTP 标准之外的自定义状态码

return 444;
  • 不发送响应头也不发送正文
  • 仅仅静默断开 TCP 连接
  • 客户端只会看到“连接已断开”之类的提示

利用这一点,可以让“明显不合理的 URL”不返回任何响应,直接断开连接。

优点很简单:

  • 请求不再到达应用层(框架 CPU 0)
  • 根据配置可完全不写入 access 日志
  • “奇怪 URL”在 nginx 层全部过滤,应用日志更干净

3. 用 blackhole.conf 统一管理规则



与其在每个 server 块里写规则,不如把公共模式集中在一个文件里。

例如:

# /etc/nginx/blackhole.conf(路径可自行调整)

# === 1. 隐藏/配置管理目录 (.git, .env, IDE 等) ===
location ~* (^|/)\.(git|hg|svn|bzr|DS_Store|idea|vscode)(/|$)   { return 444; }
location ~* (^|/)\.env(\.|$)                                    { return 444; }
location ~* (^|/)\.(?:bash_history|ssh|aws|npm|yarn)(/|$)       { return 444; }

# === 2. 未使用的 CMS 管理/漏洞扫描 ===
location ^~ /administrator/                                    { return 444; }  # Joomla 等
location ^~ /phpmyadmin/                                      { return 444; }  # 未使用则彻底拦截

# === 3. PHP/CGI/WordPress 迹象 ===
# 仅在完全不使用 PHP 的栈(如纯 Node、Go、Python)推荐
location ~* \.(?:php\d*|phtml|phps|phar)(/|$)                  { return 444; }
location ^~ /cgi-bin/                                         { return 444; }
location ~* ^/(wp-admin|wp-includes|wp-content)(/|$)          { return 444; }

# === 4. 扫描器常抓的文件/路径 ===
location ~* ^/(?:info|phpinfo|test|wso|shell|gecko|moon|root|manager|system_log)\.php(?:/|$)? {
    return 444;
}
location ~* ^/(?:autoload_classmap|composer\.(?:json|lock)|package\.json|yarn\.lock|vendor)(?:/|$) {
    return 444;
}
location ~* ^/(?:_profiler|xmrlpc|xmlrpc|phpversion)\.php(?:/|$)? {
    return 444;
}

# === 5. 备份/临时/转储文件 ===
location ~* \.(?:bak|backup|old|orig|save|swp|swo|tmp|sql(?:\.gz)?|tgz|zip|rar)$ {
    return 444;
}

# === 6. well-known: 仅允许 ACME,其他全部断开 ===
location ^~ /.well-known/acme-challenge/ {
    root /var/www/letsencrypt;
}
location ^~ /.well-known/ {
    return 444;
}

# === 7. 方法守卫(可选):几乎不使用的 HTTP 方法提前拦截 ===
# 如果不使用 TRACE、CONNECT、WebDAV 等方法,可提前阻止
if ($request_method !~ ^(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS)$) {
    return 405;
}

然后在每个 server 块里只需一行:

http {
    # ...

    server {
        listen 80;
        server_name example.com;

        include /etc/nginx/blackhole.conf;

        # 其余正常路由配置...
    }
}

这样,大多数“未使用路径的漏洞扫描”在到达应用前就被 nginx 以 444 处理。


4. 如需进一步减少日志噪声(可选)

即使已使用黑洞,仍可让这些请求不写入 access_log。可以利用 map

http {
    # 需要进入黑洞的模式,想要减少日志
    map $request_uri $drop_noise_log {
        default                            0;
        ~*^/(?:\.git|\.env)                1;
        ~*^/(wp-admin|wp-includes|cgi-bin) 1;
        ~*\.php(?:/|$)                     1;
    }

    server {
        listen 80;
        server_name example.com;

        include /etc/nginx/blackhole.conf;

        # 仅当 $drop_noise_log 为 0 时记录日志
        access_log /var/log/nginx/access.log combined if=$drop_noise_log;
    }
}
  • $drop_noise_log 为 1 的请求不记录日志。
  • 真实用户请求仍保持干净日志。

在生产环境中,建议先开启日志验证模式,确认“正常用户不会被误拦”,再切换到 if=$drop_noise_log


5. 避免误拦的指南

在制定拦截规则时,最重要的是不要过度攻击性。可参考以下准则:

  1. 仅开启适合栈的规则 * 完全不使用 PHP 的项目可直接拦截 .php。 * 若使用 Laravel、WordPress,则需去掉 .php 规则。 * 同理,若实际使用 /cgi-bin,则排除该规则。
  2. 初期可先返回 403 * 先用 return 403; 观察几天,确认无正常用户访问后再改为 444。
  3. 对危险路径保守 * .git.env、备份文件、phpinfo.php 等若泄露风险极高,直接 444 处理即可。
  4. 位置与优先级 * nginx 的 location 匹配优先级(精确、前缀、正则)需注意。 * 通常把 include blackhole.conf 放在 server 开头,主路由 location / { ... } 放在后面即可。

6. 应用、WAF、nginx:各自擅长的层面

本文介绍的方式仅是一次性噪声清理

  • nginx 黑洞:早期剔除无意义、不可存在的路径
  • 应用层验证:域名逻辑、权限、输入校验等业务专属安全
  • WAF/安全方案:基于模式/签名的攻击、L7 DDoS、精细化机器人过滤

各层应互补而非替代。nginx 黑洞是实现成本低、效果显著的“小技巧”。


结语

任何暴露到互联网的服务都难以完全避免“奇怪 URL 扫描”。

但可以从视角转变

“如果它一定会来,为什么不在应用前让它静默消失?”

通过 blackhole.conf 统一管理规则,并用 444 及时断开,

  • 应用日志更干净
  • 监控/分析更简洁
  • 服务器资源略有释放

最重要的是,凌晨 2 点打开日志,看到的不是无休止的 /wp-login.php,而是真正用户的请求,体验会更愉快。

如果你已有 nginx,赶紧试试自己的 blackhole.conf,几天后你会想:

“我为什么没早点做?”

image of nginx block malicious url