動かざることバグの如し

近づきたいよ 君の理想に

NginxでIPブロックしつつ特定のUser-Agentだけ許可する設定

環境

  • Nginx 1.31.1

やりたいこと

以下のようなNginx設定があったとする。

server {
  listen 443 ssl;
  server_name example.com;
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-SSL-Cipher $ssl_cipher;

  location / {
    proxy_pass http://myserver;
  }
}

がしかしBot攻撃が激しいのでNginxレベルでIPをブロックしたい。

その場合は以下のようになる。

location / {
  proxy_pass http://myserver;
  include /etc/nginx/block_ips.conf;
  deny all;
}

ただ、このIP一覧にGoogleBot(本物)も含まれてしまっているのでユーザーエージェントに「Googlebot」の文字があれば許可したい。

コード

まず http ディレクティブの中に map ブロックを追加して、$is_googlebot という変数を定義する。User-Agentに「Googlebot」が含まれていれば1、それ以外は0になる。

# httpディレクティブ
map $http_user_agent $is_googlebot {
    default     0;
    ~*Googlebot 1;
}

次に server ブロック側でその変数を使う。if ($is_googlebot) でGooglebotと判定された場合は、内部ロケーション /_googlebot_proxy にリライトして通す。そうでない場合はIPブロックリストの評価に進み、deny all でブロックされる。

/_googlebot_proxyinternal を付けているので外部から直接アクセスはできない。

server {
  listen 443 ssl;
  server_name example.com;
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-SSL-Cipher $ssl_cipher;

  location / {
    if ($is_googlebot) {
      rewrite ^ /_googlebot_proxy last;
    }
    proxy_pass http://myserver;
    include /etc/nginx/block_ips.conf;
    deny all;
  }

  location /_googlebot_proxy {
    internal;
    proxy_pass http://myserver$request_uri;
  }
}