管理画面や内部向けAPI、ステージング環境などを“とりあえず守る”ときに、Nginxの Basic認証 と IP制限 は即効性が高い定番手段です。
アプリ側に手を入れずに「入口(Nginx)だけでガード」をかけられるので、実務でも出番が多いです。
この記事では、
- Basic認証(ユーザー名/パスワード)でアクセスを制限する
- IP制限(allow/deny)で特定ネットワーク以外を遮断する
- 「IPがOKなら認証なし」「IPがダメなら認証必須」などの“組み合わせ”を実現する
までを、Dockerで再現できるハンズオン付きで解説します。
まず整理:Basic認証とIP制限は何が違う?
Basic認証(auth_basic)
- ブラウザがユーザー/パスワード入力を要求される方式
- 認証が通ればアクセス可能
- 使いどころ:管理画面、簡易な保護、ステージング環境、緊急の防御
注意点
- HTTPSなしで使うと、資格情報が盗まれるリスクが大きい(必ずHTTPSとセット推奨)
- “強固な認証基盤”というより、入口の簡易ガード
IP制限(allow/deny)
- クライアントのIPで通す/拒否する方式
- そもそもブロックするのでシンプル
- 使いどころ:社内ネットワーク限定、監視用エンドポイント、管理者のみの画面
注意点
- 固定IPや社内VPN前提が多い
- プロキシ/CDNを挟むと「本当のクライアントIP」をNginxが見失うことがある(後述)
Nginxの基本:IP制限は “locationごと” にかけられる
Nginxは server { ... } の中で location { ... } を分けられるので、
/は公開/adminはBasic認証/internalはIP制限
のような「パス単位の防御」が得意です。まずはこの感覚を掴むのが最短です。
ハンズオン0:準備(DockerでNginxを動かす)
ディレクトリ構成
nginx-guard/
├─ html/
│ ├─ index.html
│ └─ admin.html
├─ nginx/
│ ├─ default.conf
│ └─ .htpasswd
└─ docker-compose.yml
HTMLを用意
html/index.html
<h1>Public Page</h1>
<p>ここは誰でも見られる想定</p>
html/admin.html
<h1>Admin Page</h1>
<p>ここは保護したい</p>
ハンズオン1:Basic認証をかける(auth_basic / auth_basic_user_file)
1) .htpasswd を作る(推奨:bcrypt)
一番楽なのは htpasswd コマンドです。ローカルに無い場合でも、Dockerで作れます。
Dockerで作る例(bcrypt)
プロジェクト直下で実行:
docker run --rm httpd:2.4-alpine htpasswd -nbB admin 'StrongPasswordHere' > nginx/.htpasswd
-n:ファイルに書かず標準出力へ-b:パスワードをコマンド引数から渡す(履歴に残るので本番では注意)-B:bcrypt(推奨)
本番では
-bを避けて対話入力にする、もしくはCI/Secret管理で生成・配布するのが安全です。
生成された nginx/.htpasswd はこんな形式になります(例):
admin:$2y$05$....
2) Nginx設定を書く
nginx/default.conf
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# /admin 配下をBasic認証で保護
location /admin {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
# 例として admin.html を返す
try_files /admin.html =404;
}
}
ポイント:
auth_basicの文字列はブラウザに表示される“realm”名auth_basic_user_fileは.htpasswdのパス
3) docker-compose.yml
docker-compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/.htpasswd:/etc/nginx/.htpasswd:ro
4) 起動
docker compose up -d
5) 動作確認
http://localhost:8080/→ 認証なしで表示http://localhost:8080/admin→ ユーザー名/パスワードが要求される
curlで確認する場合:
curl -i http://localhost:8080/admin
→ 401 Unauthorized が返ればOK(認証要求)
認証あり:
curl -i -u admin:StrongPasswordHere http://localhost:8080/admin
→ 200 OK になれば成功です。
ハンズオン2:IP制限(allow/deny)をかける
次は「特定IPだけ許可、それ以外は拒否」をやります。
ローカル環境ではクライアントIPが
127.0.0.1(IPv6なら::1)になることが多いです。
まずはローカル許可で体験し、そのあと “社内CIDR” の書き方を覚えるのが安全です。
1) 設定を追記
nginx/default.conf をこうします:
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# /admin はBasic認証
location /admin {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
try_files /admin.html =404;
}
# /internal はIP制限(例:ローカルだけ許可)
location /internal {
allow 127.0.0.1;
allow ::1;
deny all;
default_type text/plain;
return 200 "internal ok\n";
}
}
ポイント:
allow/denyは上から評価されます- 最後に
deny all;を置くのが典型
2) 反映(再起動でもOK、ここではrestart)
docker compose restart nginx
3) 確認
curl -i http://localhost:8080/internal
ローカルからなら 200 が返る想定です。
実務パターン:CIDRで社内ネットワークだけ許可する
例(社内が 192.168.0.0/16 の場合):
location /internal {
allow 192.168.0.0/16;
deny all;
}
例(オフィス固定IPが 203.0.113.10 の場合):
location /internal {
allow 203.0.113.10;
deny all;
}
ハンズオン3:IPがOKなら認証不要、IPがダメなら認証必須(satisfy any)
ここが“入口設計”としてかなり便利なところです。
- 社内(許可IP)からは スッと入れる
- 社外からは パスワードが必要
- どちらも満たさない場合はブロック
Nginxでは satisfy any; を使うと実現できます。
設定例:/admin を「社内IP or Basic認証」で許可
nginx/default.conf の /admin を差し替え:
location /admin {
# どれか1つ満たせばOK(IP許可 or 認証OK)
satisfy any;
# IP制限(例:社内VPN/固定IP)
allow 127.0.0.1;
allow ::1;
# allow 203.0.113.10; # 固定IP例
# allow 192.168.0.0/16; # 社内CIDR例
deny all;
# Basic認証
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
try_files /admin.html =404;
}
重要な挙動
- 許可IPなら
auth_basicがあっても入力なしで通る - 許可IPじゃなければ、Basic認証が求められる
- IPもダメ、認証もダメなら通れない
よくある落とし穴・事故ポイント(ここは実務で効く)
1) “本当のクライアントIP” が見えていない問題
CloudflareやALB、リバプロを挟むと、Nginxから見える接続元IPが「プロキシのIP」になります。
この状態で allow/deny すると、意図せず全拒否 / 全許可になりがちです。
対策は、プロキシが付与するヘッダ(例:X-Forwarded-For や Cloudflare の CF-Connecting-IP)を使って、Nginxに“真のIP”を認識させること。
例(一般的な X-Forwarded-For を使う場合の考え方):
real_ip_header X-Forwarded-For;set_real_ip_from <信頼できるプロキシのIPレンジ>;
※ここは環境依存が強いので、VPS直置き・Cloudflare利用など前提が決まったら、その前提で設定を固めるのが安全です。
2) 403と401が混ざって混乱する
- IP制限で落ちる → 403 Forbidden
- Basic認証が必要 → 401 Unauthorized(認証ダイアログが出る)
どっちで落ちているか、curlのステータスで切り分けると早いです。
3) .htpasswd をGitに入れてしまう
ハッシュ化されていても、漏洩リスクや運用上の事故につながるので基本は避けたいです。
本番では Secret 管理(Kubernetes Secret / Docker secret / CIの秘匿変数)などに寄せるのがおすすめです。
4) パスワードを弱くしがち
Basic認証は突破される前提で運用するものではありませんが、弱いパスワードは事故の温床です。
最低限、長めのランダム文字列(英数記号)に寄せるのが安全です。
まとめ:Nginxの入口ガードは “小さく強い”
- Basic認証:すぐ使える。ステージング/管理画面の簡易保護に強い(HTTPSとセット)
- IP制限:シンプルで強力。社内限定や監視系に効く
- 組み合わせ(satisfy any):現場で気持ちよく運用できるパターンが作れる
コメントを残す