はじめに
PHP学習中、ポートフォリオ用にWebアプリを XAMPP環境 で開発していたとき、こんな状況に遭遇しました。
XAMPPとは
Windows/Mac上にApache・PHP・MySQLをまとめてインストールできるローカル開発環境です。
標準設定では http://localhost/... での動作が前提となっており、HTTPS証明書は設定されていません。
- 本番環境用の
.htaccess(HTTPS強制リダイレクト入り)をGitで管理していた - ローカル開発時は 誤って本番用設定が動かないよう、
.htaccessのファイル名を変えて無効化する運用にしていた
(例:.htaccess→.htaccess.bak) - あるとき ファイル名を変え忘れたまま
http://localhost/...でアクセスしてしまった - 気づいてファイル名を戻しても、特定のページだけ勝手に
https://にリダイレクトされて接続できない 😰
# .htaccessに記述していたHTTPS強制リダイレクト(本番用)
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
.htaccess を正しい状態に戻しても直らない。なぜ?
その原因は、ブラウザが密かに記憶していた HSTSキャッシュ にありました。
目次
HSTSとは?
HSTS(HTTP Strict Transport Security) とは、Webサーバーがブラウザに対して
「次回からこのドメインへのアクセスは必ずHTTPSで接続してください」と命令する仕組みです。
サーバーは Strict-Transport-Security というレスポンスヘッダーをブラウザに送信し、
ブラウザはその内容を 指定された有効期限(max-age)の間、内部に記憶 します。
Strict-Transport-Security: max-age=31536000; includeSubDomains
一度記憶されると、ユーザーが http:// でアクセスしようとしても、
サーバーを経由せずブラウザ自身が 307 Internal Redirect でhttpsに切り替えます。
これはセキュリティ上とても有効な仕組みですが、XAMPPのようなローカル開発環境では厄介な落とし穴になります。
なぜ問題が起きるのか
発生の流れ
① XAMPP環境でローカル開発中(localhost は http が前提)
↓
② 本番用 .htaccess(HTTPS強制リダイレクト入り)をGitで管理
ローカルでは .htaccess のファイル名を変えて無効化する運用にしていた
(例:.htaccess → .htaccess.bak)
↓
③ ファイル名を変え忘れたまま localhost でアクセスしてしまった
↓
④ XAMPPのApacheが .htaccess を読み込み、HTTPS強制リダイレクトが実行された
↓
⑤ ブラウザが「localhost = 常にHTTPS」と記憶(HSTSキャッシュに保存)
↓
⑥ ファイル名を正しく戻しても…
↓
⑦ ブラウザがキャッシュを見て勝手にhttpsへリダイレクト!ループから抜けられない
ポイント
-
.htaccessの設定を外しただけでは解決しない - ブラウザのキャッシュクリア(Ctrl+Shift+Del)でも HSTSキャッシュは消えない
- シークレットウィンドウは既存のキャッシュを参照しないため、症状が出ない
こんな状況で遭遇しやすい
| 状況 | 説明 |
|---|---|
本番用 .htaccess のファイル名変更を忘れてlocalhostにアクセスした |
今回のケース。 ファイル名を戻しても症状が残る |
.htaccess でHTTPS強制後、httpに戻したい |
本番設定をローカルで試したあと外すときにも起こりやすい |
| ローカル開発環境でhttpsループにはまった |
localhost へのアクセスが繰り返しhttpsになる |
| 特定ページだけhttpsになる | そのページだけHTTPS経由でアクセスしたことがある場合 |
解決方法①:シークレットウィンドウで確認する
まず、シークレットウィンドウ(プライベートブラウジング)で現象が再現するか確認 します。
シークレットウィンドウは既存のHSTSキャッシュを参照しないため、キャッシュが原因かどうかの切り分けに使えます。
| ブラウザ | ショートカット |
|---|---|
| Chrome / Edge | Ctrl + Shift + N |
| Firefox | Ctrl + Shift + P |
シークレットウィンドウで正常にアクセスできる → HSTSキャッシュが原因と確定
シークレットウィンドウでも同じ現象 → 別の原因(.htaccessやサーバー設定を確認)
解決方法②:HSTSキャッシュをクリアする
Chrome の場合
- アドレスバーに以下を入力してEnter
chrome://net-internals/#hsts
- ページ下部の 「Delete domain security policies」 欄にドメインを入力
# localhostの場合
localhost
# 本番ドメインの場合
example.com (※ https:// は不要)
-
「Delete」 ボタンをクリック
-
念のため 「Query HSTS/PKP domain」 欄で同じドメインを検索し、
Not foundと表示されればキャッシュのクリア完了です
Edge の場合
Chrome と同じ手順で、アドレスバーに以下を入力します。
edge://net-internals/#hsts
Firefox の場合
-
Ctrl + Shift + Hで履歴を開く - 対象のサイトを右クリック
- 「このサイトの情報を消去」 を選択
- Firefoxを再起動
注意点
⚠ サーバー側の設定も確認する
HSTSキャッシュをクリアしても、サーバーがまだHSTSヘッダーを返し続けている場合、
次のアクセス時に再びキャッシュされます。
ローカルの .htaccess が正しく無効化されているか必ず確認しましょう。
また、HSTSヘッダーを明示的に無効化したい場合は以下を設定します。
# .htaccess でHSTSを無効化する場合
# max-age=0 を指定することで、ブラウザにキャッシュを破棄させる
Header set Strict-Transport-Security "max-age=0"
⚠ 本番環境のHSTSは慎重に設定する
本番環境でHSTSの max-age を長く設定すると、
設定ミスがあった際に 長期間HTTPでのアクセスができなくなる リスクがあります。
最初は短い時間(例:300秒 = 5分)でテストすることが推奨されています。
# テスト用:有効期限を5分に設定
Header set Strict-Transport-Security "max-age=300"
# 本番用:1年
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
⚠ includeSubDomains の設定には注意
includeSubDomains を付けると、サブドメインすべてにHTTPSが強制されます。
SSL化されていないサブドメインが残っている場合、そのサブドメインにアクセスできなくなります。
⚠ HSTSプリロードリストへの登録は慎重に
preload オプションをつけてHSTSプリロードリストに登録すると、
解除には数ヶ月かかる場合があります。十分に動作確認してから登録してください。
まとめ
| やること | 手順 |
|---|---|
| 原因の切り分け | シークレットウィンドウ(Ctrl+Shift+N)で確認 |
| Chromeのキャッシュクリア |
chrome://net-internals/#hsts → Delete |
| Edgeのキャッシュクリア |
edge://net-internals/#hsts → Delete |
| ローカル側の確認 |
.htaccess のファイル名・設定内容を確認 |
| サーバー側の対処 |
.htaccess に max-age=0 を設定 |
XAMPPのようなローカル開発環境では localhost が http で動くのが前提です。
.htaccess の管理ミスが一瞬あるだけで、ブラウザにHSTSキャッシュが残り「なぜかhttpsになる」というループに陥ります。
開発中は max-age を短めに設定する習慣をつけること、そして .htaccess の有効・無効の切り替えを確実に行うことが、こういったトラブルの予防になります。
同じ状況でハマっている方の参考になれば幸いです。