HTTPS リダイレクトルールの仕組み
RewriteCond %{HTTPS} !=on [NC]
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
各行の役割
1行目:
RewriteCond %{HTTPS} !=on [NC]
現在のアクセスがHTTPS接続でないことを確認する条件。直接サーバーにHTTPSで接続している場合はこの条件に合致せず、リダイレクトは発生しない。
2行目:
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
リバースプロキシやCDN(Cloudflare、AWS ALBなど)を経由しているケースに対応するための条件。
以下のような構成の場合:
ユーザー →(HTTPS)→ CDN/プロキシ →(HTTP)→ サーバー
サーバーから見るとHTTPアクセスに見えるが、実際にはユーザーはHTTPSでアクセスしている。プロキシが付与する
X-Forwarded-Proto
ヘッダーで元のプロトコルを判定する。
この行がないと、プロキシ経由のHTTPSアクセスで 無限リダイレクトループ が発生する。
3行目:
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
上記2つの条件が両方とも真の場合(= 本当にHTTPアクセスの場合)のみ、HTTPSのURLへ301リダイレクトを実行する。
RewriteCond(条件ディレクティブ)
「次の
RewriteRule
を実行するかどうか」を判定する条件文。プログラミングにおける
if
文に相当する。
構文
RewriteCond %{テスト文字列} パターン [フラグ]
主要なテスト文字列(サーバー変数)
| 変数 | 内容 |
|---|---|
%{HTTPS} |
HTTPS接続なら
on
|
%{HTTP_USER_AGENT} |
ブラウザやbotのUA文字列 |
%{REQUEST_URI} |
リクエストされたパス(例:
/wp-admin/
)
|
%{REQUEST_FILENAME} |
サーバー上の実ファイルパス |
%{QUERY_STRING} |
?
以降のクエリ文字列
|
%{THE_REQUEST} |
生のHTTPリクエスト行(例:
GET /path HTTP/1.1
)
|
%{HTTP:ヘッダー名} |
任意のHTTPヘッダーの値 |
%{THE_REQUEST}
を使う理由と活用パターン
%{THE_REQUEST}
と
%{REQUEST_URI}
は似ているが、
Apache による URL 正規化の前後
という決定的な違いがある。
| 変数 | 値の例 | 特徴 |
|---|---|---|
%{REQUEST_URI} |
/path/to/page |
Apache が正規化した後のパス。
//
は
/
に圧縮される
|
%{THE_REQUEST} |
GET /path//to/page HTTP/1.1 |
クライアントが送信した生のリクエスト行。正規化前の情報を保持する |
ダブルスラッシュの検出
には
%{THE_REQUEST}
が必須。
%{REQUEST_URI}
では
//
がすでに
/
に圧縮されており、条件が一致しない。
# ダブルスラッシュを含む URL を 301 リダイレクトで正規化
# %{REQUEST_URI} はすでに正規化されているため、検出には %{THE_REQUEST} を使う
RewriteCond %{THE_REQUEST} \s[^\s?]*//
RewriteRule ^ %{REQUEST_URI} [R=301,L,NE]
\s[^\s?]*//
パターンの内訳:
-
\s— メソッドと URL の間の空白(GETの直後) -
[^\s?]*— 空白と?(クエリ文字列の開始)を除く任意の文字列(パス部分のみを対象にする) -
//— 検出対象のダブルスラッシュ
RewriteRuleのパターンは正規化後のREQUEST_URIに対して適用される。%{THE_REQUEST}で元のリクエスト行にダブルスラッシュが含まれているかを判定し、リダイレクト先には正規化済みの%{REQUEST_URI}をそのまま指定する。NEフラグ(noescape)でリダイレクト先の特殊文字がエスケープされるのを防ぐ。これはジェネレーターの出力と同じパターンである。
パターンの記述方法
# 正規表現マッチ
RewriteCond %{HTTP_USER_AGENT} (wget|curl|nikto) [NC]
# 否定マッチ(!をつける)
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
# ファイルが実在するか
RewriteCond %{REQUEST_FILENAME} -f
# ディレクトリが実在するか
RewriteCond %{REQUEST_FILENAME} -d
テスト演算子(-f, -d など)
パターン部分には正規表現だけでなく、ファイルシステムの状態をチェックする 特殊なテスト演算子 が使用できる。
| 演算子 | 意味 | チェック内容 |
|---|---|---|
-f |
is F ile | 通常のファイルが存在するか |
-d |
is D irectory | ディレクトリが存在するか |
-s |
is file with S ize | ファイルが存在し、かつサイズが0でないか |
-l |
is symbolic L ink | シンボリックリンクが存在するか |
-F |
is existing F ile (via subrequest) | Apacheにサブリクエストを投げて存在確認(負荷が高い) |
-U |
is existing U RL (via subrequest) | URLとしてアクセス可能か(負荷が高い) |
実務で頻出するのは
-fと-dの2つ。先頭に!を付けると否定になる(例:!-f= ファイルが存在しない、!-d= ディレクトリが存在しない)。WordPress の.htaccessでは!-fと!-dの組み合わせが頻出する。
WordPress での使用例
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
この記述は「リクエストされたパスに実際のファイルまたはディレクトリが存在する場合、URLの書き換えをせずそのまま返す」という意味。
画像・CSS・JSなどの実ファイルをWordPressのリライトエンジン(
index.php
)を経由させず直接配信するために必要な処理。この条件がないと
/wp-content/uploads/photo.jpg
のような実在ファイルへのアクセスもすべて
index.php
に転送されてしまい、画像やCSSが表示されなくなる。
複数条件の組み合わせ
RewriteCond
を複数行並べるとデフォルトで
AND条件
になる。
# 条件A AND 条件B → 両方満たしたときだけRewriteRuleが発動
RewriteCond %{HTTPS} !=on [NC]
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
OR条件にしたい場合は
[OR]
フラグを使用する。
# ファイルが存在する OR ディレクトリが存在する
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
重要:
RewriteCondは直後のRewriteRuleにのみ適用される。
RewriteRule(書き換えルール)
実際にURLを書き換えたりリダイレクトしたりする本体。
構文
RewriteRule パターン 置換先 [フラグ]
パターンと置換先
| 要素 | 説明 |
|---|---|
^(.*)$ |
任意のURLにマッチ(
()
でキャプチャ)
|
$1 |
キャプチャした内容を置換先で参照 |
%{HTTP_HOST} |
ホスト名(例:
example.com
)
|
%{REQUEST_URI} |
リクエストパス |
- |
URLの書き換えをしない(ブロック用途) |
使用例
# HTTPSリダイレクト
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# URLは書き換えず403 Forbiddenを返す(ボットブロック)
RewriteRule .* - [F,L]
# 410 Goneを返す(不正なクエリ文字列対策)
RewriteRule ^ - [R=410,L]
IfModule(モジュール存在チェック)
指定したApacheモジュールが有効な場合にのみ、中のディレクティブを実行する。
構文
<IfModule モジュール名>
# モジュールが有効なときだけ実行される
</IfModule>
必要な理由
存在しないモジュールのディレクティブを記述すると
Apacheが起動エラー(500)
になる。
IfModule
で囲むことで、モジュールがない環境でも安全にスキップされる。
主要なモジュール
| モジュール | 役割 |
|---|---|
mod_rewrite.c |
URLの書き換え・リダイレクト |
mod_deflate.c |
Gzip圧縮 |
mod_expires.c |
ブラウザキャッシュの有効期限設定 |
mod_headers.c |
HTTPヘッダーの追加・変更 |
mod_authz_core.c |
アクセス制御(Apache 2.4+) |
mime_module |
MIMEタイプの追加 |
否定(Apache 2.2 / 2.4 互換パターン)
<IfModule mod_authz_core.c>
# Apache 2.4+ の書き方
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2 以前の書き方(フォールバック)
Order deny,allow
Deny from all
</IfModule>
補足: XServerはApache 2.4系のため、実質的には
mod_authz_core.c側のみ使用される。
モジュールの有効化スイッチ
一部のモジュールは
IfModule
で囲むだけでなく、機能を有効化する
スイッチ(ディレクティブ)
が必要になる。スイッチがないと、その後に書いた設定がすべて無視される。
スイッチが必要なモジュール
| モジュール | スイッチ | 役割 |
|---|---|---|
mod_rewrite.c |
RewriteEngine On |
URL書き換え機能を有効化 |
mod_expires.c |
ExpiresActive On |
キャッシュ有効期限機能を有効化 |
# mod_rewrite の場合
<IfModule mod_rewrite.c>
RewriteEngine On ← スイッチON
RewriteCond %{HTTPS} !=on [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
# mod_expires の場合
<IfModule mod_expires.c>
ExpiresActive On ← スイッチON
ExpiresByType text/css "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 month"
</IfModule>
スイッチが不要なモジュール
mod_deflate
、
mod_headers
などはスイッチ不要で、
IfModule
の中にいきなり設定を記述できる。すべてのモジュールにスイッチがあるわけではなく、モジュールごとの仕様による。
基本パターン
IfModule でモジュールの存在確認 → スイッチON → 設定記述
この3ステップがセットになっているのが
.htaccess
のお決まりの構文。
補足:
RewriteEngine Offのように明示的に無効化することも可能。デバッグ時にリライトルールを一括で停止したい場合に使える。補足:
<IfModule mod_rewrite.c>ブロックが複数ある場合は、各ブロックでそれぞれRewriteEngine Onを記述する。ブロックごとに独立しているため、改めてONにする必要がある。
RewriteCond のフラグ
| フラグ | 名称 | 説明 |
|---|---|---|
[NC] |
No Case | 大文字小文字を区別しない |
[OR] |
OR条件 | デフォルトのAND条件をORに変更する |
RewriteRule のフラグ
使用頻度の高いフラグ
| フラグ | 名称 | 説明 |
|---|---|---|
[L] |
Last |
現在のリライトパスを終了し、以降のルールを評価しない。ただし URI が書き換わった場合、Apache は内部的に
.htaccess
を再評価する。
[R]
付き(外部リダイレクト)の場合はクライアントへレスポンスを返して処理が完結するため、この再評価は発生しない
|
[R=コード] |
Redirect |
指定したHTTPステータスコードでリダイレクト(例:
R=301
,
R=410
)。コードを省略した
[R]
のみの場合はデフォルトで 302 になる
|
[F] |
Forbidden | 403 Forbiddenを返す |
[NE] |
No Escape | 置換先URLをエンコードしない(二重エンコード防止) |
[T=型] |
Type |
MIMEタイプを強制指定する(例:
T=image/webp
)
|
その他のフラグ
| フラグ | 名称 | 説明 |
|---|---|---|
[QSA] |
Query String Append | リダイレクト先に元のクエリ文字列を引き継ぐ |
[QSD] |
Query String Discard | クエリ文字列を破棄する |
[P] |
Proxy | リダイレクトではなくプロキシとして転送 |
[G] |
Gone |
410 Goneを返す(
[R=410]
のショートハンド)
|
[CO] |
Cookie | Cookieをセットする |
[E] |
Environment | 環境変数をセットする |
フラグの組み合わせ
フラグはカンマ区切りで複数指定できる。
# 301リダイレクト + 処理終了 + エンコードしない
RewriteRule ^ %{REQUEST_URI} [R=301,L,NE]
[L]は基本的にほぼ毎回付ける。付け忘れると後続ルールが意図せず適用される原因になる。
3つのディレクティブの関係性
IfModule → 「このモジュールは使える?」
└ RewriteCond → 「この条件に合致する?」
└ RewriteRule → 「条件を満たしたのでこう処理する」
参考: HTTP ステータスコード(.htaccess で使用するもの)
| コード | 意味 | 用途 |
|---|---|---|
| 301 | Moved Permanently | 恒久的なリダイレクト(HTTPS強制、URL正規化) |
| 302 | Found | 一時的なリダイレクト |
| 403 | Forbidden | アクセス拒否(ボットブロック、ファイル保護) |
| 404 | Not Found |
リソースが存在しない(
ErrorDocument 404
でカスタムエラーページを指定)
|
| 410 | Gone | リソースが完全に削除済み(不正リクエスト対策) |