Closed13

Nginxをリバースプロキシサーバとして使う際に、Basic認証を設定したい

magumagu

Laravel + Next.jsのSPAアプリを開発中。

Nginxをリバースプロキシとして使って、フロントとAPIにそれぞれリクエストを振り分けるところまでできた。

開発中の最低限のセキュリティとしてBasic認証を付けたかったので、その実装を試してみる。

magumagu

現在の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";
    }
}
magumagu

default.confに以下を追加。.htpasswdにはあらかじめ設定したユーザー名とパスワード(のハッシュ値)が記載済み。

    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
magumagu

http://localhostにアクセスして、設定したアイパスを入力、、、したが、なぜか403エラーに

magumagu

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/"

magumagu

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
magumagu

今度はBasic認証成功!

...が、なんかAPI呼ぶたびにBasic認証が求められてる。
このアプリでは継続的にユーザー情報取得のためにバックエンドAPIを呼ぶので、その度にBasic認証のポップアップが出てきてとてもウザい、、、

magumagu

API関係なく、ページをリロードするたびBasic認証が求められてる。
ブラウザが記憶してくれるんじゃなかったっけ、、、?

magumagu

一度ログインしたら求められなくなった。

リクエストヘッダにAuthorizationヘッダがちゃんと付いてる = ブラウザで記憶されてセットされてる。
ログイン前と後で挙動が変わるのか?

magumagu

どうやら、Chrome(またはChronium系)ブラウザ固有の問題で、401エラーを返すとBasic認証のダイアログが再び表示されてしまうらしい。Authorizationヘッダも削除されている模様。
試しにSafariで同じ操作をしてみると、ログイン前でも一度しか認証を求められず、その後のリクエストは必ずAuthorizationヘッダが付与されていた。

https://sanzo83.hatenablog.com/entry/2021/01/26/115902
https://serverfault.com/questions/182226/htaccess-requires-password-in-chrome-but-not-other-browsers

magumagu

つまり、根本の原因はBasic認証とアプリ側のユーザー認証を2重で行っていたことにありそう。以下のような流れ。

ユーザーがhttp://localhostにアクセス

401エラーを返す & ブラウザがBasic認証ダイアログ表示

ユーザーがBasic認証情報入力

画面表示

JSがユーザー認証用のバックエンドAPIを呼ぶ

未ログイン状態なので401エラーを返す

ChromeがメモリのBasic認証情報を削除 &(ここは推測だけど)Authorizationヘッダを削除

Basic認証ダイアログを再び表示

magumagu

そもそも、アプリ側で認証してるならBasic認証する必要はないよね...当たり前だけど反省。
Basic認証は削除することにした。

今回の学び:
・Basic認証とアプリのユーザー認証を2重にすると、ブラウザによっては(特にChrome)Basic認証の情報がメモリに保存されずに、ダイアログが毎回表示されることがある
・Basic認証とアプリのユーザー認証は、どちらか片方を採用する
 ・とはいえ、Basic認証はあくまで簡易的な認証なので、アプリ側の認証を採用すべき

magumagu

何かツッコミどころあったら書き込んで欲しいです🙇

このスクラップは15時間前にクローズされました