Nginxをリバースプロキシサーバとして使う際に、Basic認証を設定したい
Laravel + Next.jsのSPAアプリを開発中。
Nginxをリバースプロキシとして使って、フロントとAPIにそれぞれリクエストを振り分けるところまでできた。
開発中の最低限のセキュリティとしてBasic認証を付けたかったので、その実装を試してみる。
現在のnginxのdefault.conf設定はこれ。
server {
listen 80;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
# APIルートはLaravelに振り分け
location /api/ {
proxy_pass http://laravel:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Requested-With $http_x_requested_with;
}
# Laravel SanctumのCSRFトークン取得エンドポイント
location /sanctum/csrf-cookie {
proxy_pass http://laravel:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Requested-With $http_x_requested_with;
}
# その他のリクエストはNext.jsに振り分け
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocketサポート(Next.jsのHMR用)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
default.conf
に以下を追加。.htpasswd
にはあらかじめ設定したユーザー名とパスワード(のハッシュ値)が記載済み。
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
http://localhost
にアクセスして、設定したアイパスを入力、、、したが、なぜか403エラーに
docker管理してるのでログを確認してみたら、.htpasswd
がうまくマウントされてないっぽい
docker logs --tail=100 -f nginx_server
2025/09/07 13:28:52 [error] 29#29: *11 open() "/etc/nginx/conf.d/.htpasswd" failed (2: No such file or directory), client: 172.18.0.1, server: , request: "GET /favicon.ico HTTP/1.1", host: "localhost", referrer: "http://localhost/"
docker-compose.yaml
に記載したと思ってたけど、リビルドしていなかった、、、
# Nginx Web Server
nginx:
image: nginx:alpine
container_name: nginx_server
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./:/var/www
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./docker/nginx/.htpasswd:/etc/nginx/conf.d/.htpasswd:ro
networks:
- app-network
depends_on:
- laravel
今度こそリビルド
docker compose up nginx --build
ちゃんとマウントされてるのを確認
docker exec -it nginx_server /bin/sh
/ # ls -la /etc/nginx/conf.d/
total 20
drwxr-xr-x 1 root root 4096 Sep 7 13:31 .
drwxr-xr-x 1 root root 4096 Aug 13 20:42 ..
-rw-r--r-- 1 root root 44 Sep 7 13:01 .htpasswd
-rw-r--r-- 1 root root 1522 Sep 7 13:33 default.conf
今度はBasic認証成功!
...が、なんかAPI呼ぶたびにBasic認証が求められてる。
このアプリでは継続的にユーザー情報取得のためにバックエンドAPIを呼ぶので、その度にBasic認証のポップアップが出てきてとてもウザい、、、
API関係なく、ページをリロードするたびBasic認証が求められてる。
ブラウザが記憶してくれるんじゃなかったっけ、、、?
一度ログインしたら求められなくなった。
リクエストヘッダにAuthorizationヘッダがちゃんと付いてる = ブラウザで記憶されてセットされてる。
ログイン前と後で挙動が変わるのか?
どうやら、Chrome(またはChronium系)ブラウザ固有の問題で、401エラーを返すとBasic認証のダイアログが再び表示されてしまうらしい。Authorizationヘッダも削除されている模様。
試しにSafariで同じ操作をしてみると、ログイン前でも一度しか認証を求められず、その後のリクエストは必ずAuthorizationヘッダが付与されていた。
つまり、根本の原因はBasic認証とアプリ側のユーザー認証を2重で行っていたことにありそう。以下のような流れ。
ユーザーがhttp://localhostにアクセス
↓
401エラーを返す & ブラウザがBasic認証ダイアログ表示
↓
ユーザーがBasic認証情報入力
↓
画面表示
↓
JSがユーザー認証用のバックエンドAPIを呼ぶ
↓
未ログイン状態なので401エラーを返す
↓
ChromeがメモリのBasic認証情報を削除 &(ここは推測だけど)Authorizationヘッダを削除
↓
Basic認証ダイアログを再び表示
そもそも、アプリ側で認証してるならBasic認証する必要はないよね...当たり前だけど反省。
Basic認証は削除することにした。
今回の学び:
・Basic認証とアプリのユーザー認証を2重にすると、ブラウザによっては(特にChrome)Basic認証の情報がメモリに保存されずに、ダイアログが毎回表示されることがある
・Basic認証とアプリのユーザー認証は、どちらか片方を採用する
・とはいえ、Basic認証はあくまで簡易的な認証なので、アプリ側の認証を採用すべき
何かツッコミどころあったら書き込んで欲しいです🙇