.htpasswd ファイルの作り方:HTTP Basic 認証 完全ガイド
.htpasswd ファイルは HTTP Basic 認証のサーバー側の認証情報ストアで、各行が username:hash のペアになったプレーンテキストファイルだ。.htpasswd ファイルを作るというのは、このハッシュ化された行を生成して、Web サーバーが読める場所に置くことを指す。やり方は三通り。
htpasswdコマンド(apache2-utils/httpd-toolsに含まれる)。いわば本家のツール。openssl passwd。ほぼどこにでも最初から入っていて、追加パッケージが要らない。- ブラウザ内で生成。htpasswd ジェネレーターなら、インストールもネットワーク送信もなしでローカルにエントリを作れる。
このガイドはワンライナーで終わらせない。Basic 認証のハンドシェイクが実際にどう動くのか、ファイルを三通りで作る方法、五つのハッシュ形式のどれを選ぶか、Apache・nginx・Docker・Kubernetes・Caddy・Traefik への組み込み方、そして認証情報ファイルを誰でもダウンロードできる状態で公開しないための締め方まで扱う。
.htpasswd ファイルとは
.htpasswd ファイルの各行は、コロン区切りのペアで一人ぶんの認証情報を保持する。ユーザー名はそのまま入るが、パスワードは一方向ハッシュとしてしか入らないので、平文がディスクに書かれることはない。bcrypt の 1 行を分解すると次のようになる。
admin : $2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
│ │
└─ username └─ hash (algorithm prefix $2y$ + cost + salt + digest)
先頭にユーザー名、次に 1 個の :、その後にハッシュが続く。ユーザー名は最大 255 バイト、コロンはフィールド区切りなので含めてはならない。ハッシュは先頭にアルゴリズムマーカー(bcrypt なら $2y$、Apache MD5 なら $apr1$、SHA-1 なら {SHA})を持つため、サーバーは追加設定なしで検証方法を見分けられる。
複数ユーザーなら、ユーザーごとに 1 行を足す。
admin:$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
alice:$2y$10$3bQ8xY7tLp2mZ0xW5cR4fO9vK1jH6sD2nG8aQ5wE3rT7uI4oP1cm
bob:$apr1$mZ0xW5cR$4fK1jH6sD2nG8aQ5wE3rT2
同じファイル内でアルゴリズムを混ぜてもかまわない。サーバーは各行を読み、先頭の接頭辞から形式を判別して、その形式で検証する。上の例では bcrypt のユーザー 2 人と apr1 のユーザー 1 人が問題なく共存している。
.htpasswd と .htaccess の違い
この二つは Apache でセットで使うため混同されがちだが、役割は別物だ。.htaccess は Apache のディレクトリ単位の設定ファイルで、Basic 認証を有効にして認証情報ストアを指し示す「ディレクティブ」を保持する。.htpasswd のほうは認証情報データベースで、中身は username:hash の行だけ、設定は一切含まない。
つまり .htaccess が「このディレクトリにログインを要求する」ことと「ユーザー一覧の場所」を決め、.htpasswd がそのユーザー一覧そのものになる。nginx は .htaccess を使わない。Basic 認証の設定はメイン設定の server か location ブロックに書くが、読み込む認証情報は同じ .htpasswd 形式だ。
HTTP Basic 認証の仕組み
HTTP Basic 認証は HTTP 仕様に組み込まれたチャレンジ・レスポンス型のハンドシェイクだ。次の三つのステップさえ押さえれば、後のトラブルシューティングはほとんど見通せる。
- クライアントが認証情報なしで保護リソースを要求する。
- サーバーが
401 Unauthorizedを返し、WWW-Authenticate: Basic realm="..."ヘッダーを添える。これがチャレンジだ。 - クライアントが
Authorization: Basic <base64(user:password)>ヘッダーを付けてリクエストを再送する。 認証情報が.htpasswdファイルのある行と一致すれば、サーバーはリソースを返す。
プロトコルはこれで全部だ。ログインフォームもセッション Cookie もトークンもない。以降のリクエストはどれも同じヘッダーを運ぶ。
401 / WWW-Authenticate のチャレンジ
WWW-Authenticate ヘッダーの役割は二つ。Basic トークンはどのスキームを使うかをクライアントに伝え、realm 文字列は保護領域にラベルを付ける。ブラウザは realm のテキストをログインダイアログに出し(「このサイトの内容:……」)、それをキャッシュキーとして使う。ある realm に入力した認証情報は同じ realm 内の別の URL にも使い回されるので、ユーザーはページごとに入力し直さずに済む。
curl -i で捕捉した生のやり取りは次のとおり。
$ curl -i https://example.com/admin/
HTTP/2 401
www-authenticate: Basic realm="Restricted Area"
$ curl -i -u admin:s3cret https://example.com/admin/
HTTP/2 200
Authorization: Basic ヘッダー
クライアントが送る認証情報は base64(username:password) だ。ここに Basic 認証で最も大事なセキュリティ上の事実がある。base64 は符号化であって暗号化ではない。誰でも完全に元へ戻せるので、認証情報は事実上平文のまま流れている。この往復は自分の手で確かめられる。
# Encode the credential the way a browser does
printf 'admin:s3cret' | base64
# → YWRtaW46czNjcmV0
# Anyone who captures the header can decode it instantly
printf 'YWRtaW46czNjcmV0' | base64 -d
# → admin:s3cret
この可逆性こそ、Basic 認証を必ず HTTPS 上で動かさなければならない理由だ。TLS がなければ、パスワードは経路上の誰にでも読み取れてしまう。このヘッダーを手で組み立てたり中身を確認したりしたいなら、Base64 エンコーダー/デコーダーが同じ user:password の変換をブラウザ内でこなしてくれる。
.htpasswd ファイルの作り方
ファイルを作る実用的な方法は三通り。手元に何が入っているか、パスワードをどこに置きたいかで選ぶとよい。
htpasswd コマンドを使う
htpasswd バイナリは Apache のユーティリティパッケージに同梱されている。まずインストールする。
# Debian / Ubuntu
sudo apt install apache2-utils
# RHEL / CentOS / Fedora
sudo yum install httpd-tools
ファイルと最初のユーザーを作る。-c フラグは「作成」の意味で、既存ファイルを上書きする。使うのはいちばん最初の一回だけにすること。
htpasswd -c /etc/nginx/.htpasswd admin
# prompts twice for the password, then writes the file
ユーザーを追加するときは -c を外し、上書きではなく追記にする。
htpasswd /etc/nginx/.htpasswd alice
プラットフォーム既定ではなく bcrypt を強制するなら -B を付ける。ファイルに触れず標準出力へエントリを出したいときは、-b(コマンドライン上のパスワード)と -n(ファイルなし)を組み合わせる。設定や Dockerfile へパイプするのに便利だ。
htpasswd -Bbn admin 's3cret'
# → admin:$2y$10$N9qo8uLOickgx2ZMRZoMye...
実際に使うフラグはこのあたり。
| フラグ | 意味 |
|---|---|
-c | 新規ファイルを作成(存在すれば上書き)。最初のユーザーのみ |
-B | bcrypt を使う |
-b | パスワードをコマンドライン引数として受け取る(プロンプトなし) |
-n | ファイルに書かず標準出力へ表示 |
-D | 指定したユーザーをファイルから削除 |
-b には一つ落とし穴がある。パスワードがシェルの履歴に残る。一度きりの本番用認証情報なら、プロンプト形式か後述のブラウザ方式のほうがよい。
apache2-utils なしで OpenSSL を使う
htpasswd バイナリがない場合。OpenSSL はほぼどのシステムにも入っていて、apr1 ハッシュを直接生成できる。printf で包めば完全な 1 行になる。
printf "admin:$(openssl passwd -apr1 's3cret')\n" >> /etc/nginx/.htpasswd
# admin:$apr1$k3l4Hj9.$qN8vY7tLp2mZ0xW5cR4f.
apr1 形式は Apache でも nginx でも可搬性があり、最小構成のマシンでは依存関係がいちばん少ない。
ブラウザ内で生成する
パッケージを入れたくない、本番パスワードを ~/.bash_history に残るシェルへ打ち込みたくない。そういうときはエントリをクライアント側で生成すればいい。htpasswd ジェネレーターは bcrypt・apr1・SHA-1 のハッシュをすべてブラウザ内で計算し、貼り付けるだけの user:hash 行と対応するサーバー設定ブロックを返す。何も送信しない。ついでにパスワードは使い回さず、ランダムパスワードジェネレーターで一意なものを作っておくといい。
htpasswd のパスワード形式を比較する
htpasswd コマンドは五つの形式を出力できるが、中身は同等ではない。どれを選ぶかは次の表が早い。
| 形式 | 接頭辞 | ソルトあり | 強度 | 用途 |
|---|---|---|---|---|
| bcrypt | $2y$ | あり | 最強 | Apache, Docker Registry, Caddy, Traefik |
| apr1(Apache MD5) | $apr1$ | あり | 中程度 | nginx(可搬で安全な既定) |
| SHA-1 | {SHA} | なし | 弱い | レガシー互換のみ |
| crypt(DES) | (なし) | あり(2 文字) | 非常に弱い | 使わないこと |
| plain | (なし) | なし | なし | ローカルテストのみ |
表のセルに収まらない補足を足しておく。bcrypt はランダムな 16 バイトのソルトと適応的なコストファクター(既定 10、最新の推奨は 12)を使うので、同じパスワードでも毎回異なるハッシュになり、作業係数はハードウェアに合わせて上げ下げできる。癖が一つあって、bcrypt はパスワードを 72 バイトで切り詰める。それを超えた分は黙って捨てられる。apr1 はソルト付き MD5 を 1,000 ラウンド回す。bcrypt よりかなり弱いものの、Apache も nginx もネイティブに実装しているおかげで可搬性が高い。SHA-1 はソルトがないため同じパスワードが同じダイジェストになり、レインボーテーブルが効いてしまう。レガシーシステム専用にとどめること。crypt と plain は歴史的な経緯とテストのために残っているだけで、本番には出さない。
$2a$ / $2b$ / $2y$ の接頭辞
bcrypt のハッシュは $2a$・$2b$・$2y$ のどれかで始まる。中身は同じアルゴリズムで、生成されるハッシュは等価で互いに交換できる。バージョン文字は、一部のライブラリが上位ビット文字や文字列長をどう扱うかをめぐる過去のバグ修正の名残にすぎない。Apache の htpasswd は $2y$ を出力し、Caddy・Traefik・Docker Registry はどれもそれを正しく検証する。
bcrypt と最近の代替手段をもっと詳しく比べたいなら、bcrypt と Argon2 と scrypt の比較ガイドが、これらのパスワードハッシュアルゴリズムがコスト・メモリ硬度・脅威モデルの面でどう違うかを解説している。
サーバーで Basic 認証を設定する
認証情報ファイルは単体では何もしない。サーバーに「これを要求せよ」と伝える必要がある。ここでは六つのプラットフォームを取り上げる。
Apache(.htaccess)
保護したいディレクトリ内の .htaccess ファイル(または vhost の <Directory> ブロック)に次を書く。
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
AuthName はブラウザが表示する realm 文字列、AuthUserFile は認証情報ファイルへの絶対パス、Require valid-user はそこに記載された任意のユーザーを受け入れる。
nginx(auth_basic)
nginx は設定を location か server ブロックに置く。.htaccess は使わない。
location /admin/ {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
nginx -s reload で再読み込みする。ここでは apr1 形式を使うこと。nginx は bcrypt の検証をシステムの crypt() に任せるが、多くのビルドでこれが失敗する(詳しくは後段のトラブルシューティングで)。apr1 ならどのプラットフォームでも内部で検証される。
Docker Registry と Kubernetes ingress-nginx
プライベートな Docker Registry の htpasswd バックエンドは bcrypt しか受け付けない。エントリを生成し、マウントし、レジストリにそのパスを指し示す。
# Generate a bcrypt entry into a file
htpasswd -Bbn admin 's3cret' > auth/htpasswd
# Run the registry with that file
docker run -d -p 5000:5000 \
-v "$(pwd)/auth:/auth" \
-e REGISTRY_AUTH=htpasswd \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2
Kubernetes ingress-nginx では、ファイルを Secret として保存し、アノテーションで参照する。
kubectl create secret generic basic-auth --from-file=auth=./auth/htpasswd
metadata:
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
Secret のキーは必ず auth という名前にすること。ingress-nginx はそのキー名を決め打ちで探す。
Caddy と Traefik
どちらも bcrypt ハッシュを前提にしている。Caddy は basic_auth ディレクティブを使う(平文ではなく bcrypt ハッシュを貼り付ける)。
example.com {
basic_auth /admin/* {
admin $2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
}
}
Traefik は basicauth ミドルウェアを使い、user:bcrypt-hash のペアを与える(設定形式に応じて $ をエスケープすること)。
http:
middlewares:
admin-auth:
basicAuth:
users:
- "admin:$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
エンドポイントを保護したら、コマンドラインから動作を確認する。cURL コマンドビルダーが -u user:pass 付きのリクエストを組み立ててくれるので、認証前の 401 と認証後の 200 を両方確かめられる。
セキュリティのベストプラクティス
Basic 認証は単純なぶん、つまずきうる箇所も限られていて見つけやすい。
- 必ず HTTPS で配信する。 認証情報は元へ戻せる base64 なので、平文の HTTP ではパスワードが回線上に丸見えになる。保護対象のエンドポイントの前段で必ず TLS を終端すること。例外はない。
- ファイルは Web ルートの外に置く。
.htpasswdが配信対象のディレクトリにあると、設定ミス一つでダウンロードされかねない。/etc/nginx/.htpasswdのような場所に置いてchmod 640をかけ、所有者を Web サーバーのユーザー(www-data、nginx)にする。サーバーは読めて他のアカウントは読めない状態にしておく。 - 強固で一意なパスワードを使う。 各アカウントにはランダムパスワードジェネレーターで作った高エントロピーのパスワードを当て、使い回さない。「十分に強い」がビットで何を意味するのか知りたいなら、パスワードエントロピー解説がその数学を噛み砕いている。
- 限界を知る。 Basic 認証にはログアウトもセッションもない。ブラウザは realm ごとに認証情報をキャッシュし、閉じるまで保持して、リクエストのたびに再送する。ハッシュやヘッダー、検証まわりの広めのチェックリストは、Web セキュリティのベストプラクティスを参照してほしい。
よくあるエラーのトラブルシューティング
nginx: crypt_r() failed (22: Invalid argument)
これは nginx の Basic 認証でいちばん多い失敗で、原因はいつも同じだ。nginx が bcrypt($2y$)ハッシュを、Blowfish スキームを持たない libc 上で検証しようとしている。典型的には Alpine の musl や古い glibc だ。対処はエントリを apr1 で生成し直すこと。apr1 ならどのプラットフォームでも nginx が内部で検証する。
printf "admin:$(openssl passwd -apr1 's3cret')\n" > /etc/nginx/.htpasswd
nginx -s reload
bcrypt 対応の libc を持つベースイメージに切り替えても直るが、apr1 のほうが単純で可搬性も高い。
正しいパスワードなのに 401 になる
パスワードは合っているはずなのに 401 が返るときは、次を上から順につぶしていく。
- ファイルパス。
AuthUserFile/auth_basic_user_fileが実在のファイルを指しているか確認する(絶対パスで、打ち間違いがないか)。 - 権限。 Web サーバーのユーザーがそのファイルを読める必要がある。
sudo -u www-data cat /etc/nginx/.htpasswdで確かめる。 - 改行コードとエンコーディング。 Windows で編集したファイルには、ハッシュを壊す
\rが混じることがある。file .htpasswdを実行し、必要ならdos2unixをかける。 - 古いブラウザキャッシュ。 ブラウザは realm ごとに認証情報をキャッシュする。プライベート/シークレットウィンドウでテストして、ブラウザが覚えている古いパスワードの線を消す。
- ハッシュの不一致。 保存されたハッシュが本当にそのパスワードと一致するか確かめる。設定を疑う前に、両方を htpasswd ジェネレーターの検証モードに貼り付けて照合する。
Basic 認証を使うべきではない場面
用途を絞れば Basic 認証は正しい道具になる。ステージングサイト、内部の管理パス、CI の成果物エンドポイント、メトリクスダッシュボード、プライベートレジストリといったところだ。依存関係ゼロで、設定は 2 分で終わる。
逆にプロダクトのログインには向かない。ログアウトもパスワードリセットもレート制限もアカウントロックも MFA もない。認証情報はリクエストごとに再送され、ブラウザが閉じるまでキャッシュされる。エンドユーザーがサインインするものなら、セッションや OAuth、OIDC を選ぶべきだ。この線引きを守ることが、Basic 認証を本来向いている場所で使い続けるコツになる。
FAQ
.htpasswd ファイルの 1 行には実際に何が入っているのか?
コロンで区切られた username:hash のペアだ。ハッシュはアルゴリズム接頭辞(bcrypt なら $2y$、Apache MD5 なら $apr1$、SHA-1 なら {SHA})で始まり、続いてソルトとダイジェストが並ぶ。平文のパスワードがファイルに現れることはない。
.htpasswd と .htaccess の違いは?
.htaccess は Apache のディレクトリ単位の設定ファイルで、Basic 認証を有効にして認証情報ストアを指し示す。.htpasswd はその認証情報ストアそのもので、username:hash の行を保持する。nginx は .htpasswd 形式を使うが、認証の設定は .htaccess ではなく server/location ブロックに書く。
.htpasswd ファイルでユーザーを追加・変更・削除するには?
追加・変更するには -c を付けずに htpasswd /path/.htpasswd username を実行する。そのユーザーが既にいれば、ハッシュが更新される。削除は htpasswd -D /path/.htpasswd username。-c はファイル全体を上書きするので、いちばん最初のユーザーにだけ使う。
ブラウザはどうやって Basic 認証の認証情報を覚え、ユーザーはどうログアウトするのか?
ブラウザは realm をキーに認証情報をキャッシュし、一致するリクエストでは自動で再送する。標準のログアウトは存在しない。消す方法はブラウザを閉じるか、そのキャッシュを消去するかのどちらかだけだ。このログアウトのなさが、Basic 認証がプロダクト認証に向かない理由の一つになっている。
同じ .htpasswd ファイルを Apache と nginx の両方で使えるか?
使える。ただしハッシュ形式が両方でサポートされていることが条件だ。apr1(Apache MD5)は Apache でも nginx でもネイティブに検証されるので、共有用にはいちばん安全な選択肢になる。bcrypt は Apache では動くが、nginx ではシステムの crypt() に依存するため、Alpine/musl ビルドでは失敗する。
HTTP Basic 認証は 2026 年でもまだ有効なのか?
有効だ。HTTPS の上に重ねる軽いゲートとしては、内部ツールやステージング環境、プライベートレジストリ、監視エンドポイントで今も実用的で、依存関係もない。ただし、セッションやリセット、レート制限、MFA が要るユーザー向けのプロダクト認証と取り違えないこと。そこは Basic 認証の守備範囲ではない。
Go Tools チームによる検証済み:本ガイドのすべてのコマンド、設定ブロック、ハッシュ形式は、Apache の htpasswd(apache2-utils)と OpenSSL のリファレンス出力と照合して確認した。