惡意機器人無法阻止,改在應用前切斷——在 nginx 階段整理奇怪 URL

nginx blackhole.conf整理奇怪 URL

當把網頁應用公開到網際網路時,無論使用哪個框架,奇怪的請求都會湧入。

  • 連不存在的 /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 連線
  • 客戶端只會看到「連線已斷開」的訊息

利用這個,我們可以把「100% 奇怪的 URL」完全不回應,直接斷開。

優點很簡單:

  • 不會到達應用層(框架 CPU 0)
  • 依設定可完全不寫入 access 日誌
  • 所有「奇怪 URL」都在 nginx 一層過濾,應用日誌變得乾淨

3. 用一個 blackhole.conf 管理規則



不想每個 server block 都寫相同規則,我通常會放一個 blackhole.conf,把共通模式集中起來。

範例:

# /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. 方法保護(選項):幾乎不使用的方法先阻斷 ===
# TRACE、CONNECT、WebDAV 系列方法若不使用,提前阻斷
if ($request_method !~ ^(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS)$) {
    return 405;
}

把這個檔案放好後,只需在每個 server block 加一行:

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 日誌
        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. 先用 404/403 測試 * 先用 return 403;return 404;,觀察幾天是否真的不會被正常使用者觸發,再改成 444。

  3. 對危險路徑保守 * .git.env、備份檔、phpinfo.php 等,即使誤封也不會影響運營

  4. 位置與優先順序 * nginx 的 location 匹配優先順序(精確、前綴、正則)很重要。 * 通常把 include blackhole.conf 放在 server block 的最前面,之後再放主路由 location / { ... },即可避免覆蓋。


6. 應用、WAF、nginx:各自擅長的地方

本文介紹的方式僅是「第一層噪音過濾」的手段。

  • nginx 黑洞:剔除「不應存在」的路徑,減少不必要的連線。
  • 應用層驗證:域名邏輯、權限、輸入驗證等服務專屬安全。
  • WAF/安全解決方案:模式/簽名攻擊、L7 DDoS、精細機器人過濾。

各層不應互相取代,而是互補。nginx 黑洞是最簡單、最有效、成本低的技巧,值得先行部署。


結語

公開到網際網路的服務,無法完全避免「奇怪 URL」掃描。但可以改變觀點:

「不管怎樣都會來,先在 nginx 前靜靜地把它們切斷」

用一個 blackhole.conf 管理共通規則,並以 444 回應,能讓應用日誌更乾淨、監控更簡單、伺服器資源更寬裕。

最重要的是,當你凌晨 2 點打開日誌,看到的不是無盡的 /wp-login.php 行列,而是「真正使用者」的請求,這種感覺真的很愉快。

如果你已經有 nginx,趕快試試這個小技巧,幾天後你會想:「為什麼我沒早點做?」

image of nginx block malicious url