リダイレクトの設定方法

はじめに

本ページでは、.htaccess を使ったリダイレクトの設定方法について解説します。.htaccess の書き方を網羅した記事は、.htaccess の書き方を参照してください。

.htaccess を使ったリダイレクトでは、いくつかのディレクティブ (プログラムの構成要素) を用いて行います。それらのディレクティブについて説明し、最後に実践的なレシピについても紹介します。

RewriteEngine ディレクティブ

RewriteEngine ディレクティブは、URL の書き換えを行うエンジンです。本ディレクティブを有効、または無効にすることで、URL の書き換え処理を制御できます。デフォルトの設定値は Off になっているため、URL の書き換え処理を行う場合は、On にする必要があります。

# RewriteEngine を起動
RewriteEngine On
RewriteEngine を起動する例

本ディレクティブを On にすることで、新しい環境変数 SCRIPT_URISCRIPT_URL が追加されます。

RewriteBase ディレクティブ

RewriteBase ディレクティブは、RewriteRule ディレクティブで指定するリダイレクト先の URL のベースとなるディレクトリを相対パスで指定できます。RewriteBase ディレクティブで URL を指定しない場合、RewriteRule ディレクティブのベースとなる URL は .htaccess が置かれたディレクトリからの相対パスとなります。

/www
|-- .htaccess
|
|-- /contents
    |-- index.html
    |-- index.php
RewriteBase を行う配置例
# RewriteEngineを起動
RewriteEngine On

# ベースとなる変更前の URL(.htaccessを格納しているディレクトリ)
# /www (http://murashun.jp)

# RewriteBase でベースとなる URL を変更
# /www/contents (https://murashun.jp/contents)
RewriteBase /contents

# RewriteRule で index.php にリダイレクトする。
# RewriteBase で変更された URL は index.php にのみ影響します。
RewriteRule contents/index.html index.php [R=301,L]
contents ディレクトリの index.html を index.php に書き換える

失敗しやすい記述例として、以下があります。RewriteBase が影響するのは RewriteRule のリダイレクト先のパスだけであるという点に注意して下さい。以下の例では、どのディレクトリの index.html にアクセスしても、/contents/index.php にリダイレクトされてしまいます。

# RewriteEngine を起動
RewriteEngine On

# RewriteBase でベースとなる URL を変更
RewriteBase /contents

# RewriteRule で index.php にリダイレクトする。
RewriteRule index.html index.php [R=301,L]
contents ディレクトリの index.html を index.php に書き換える失敗例

RewriteRule ディレクティブ

RewriteRule ディレクティブは、URL の書き換えを行うディレクティブです。本ディレクティブを使用することで、URLを書き換えや、別のURLに移動(リダイレクト)させることでアクセスされたリソースに対してWebサーバの挙動を制御できます。

本ディレクティブでは、条件に一致するURLを、後続の "置換文字列" で書き換えます。また、書き換え処理に関しては、挙動を制御するためのフラグがいくつか用意されており、柔軟な書き換えルールが定義できるようになっています。複数のフラグを指定する場合は、"," で区切って指定します。

# RewriteEngine を起動
RewriteEngine On

# RewriteRule 条件 置換文字列 [フラグ]
RewriteRule sample1.html sample2.html [L]
sample1.html を sample2.html に書き換える
RewriteRule で利用可能なフラグ一覧
フラグシンタックス説明
B書き換え処理の前に英数字以外の文字をエスケープ(パーセントエンコーディング)します。
Cchain条件に一致したら、次の行に書かれた処理を連続して行います。これは通常の動作となるため、このフラグの用途は、条件が不一致の場合に後続の条件をスキップするために使われます。
CO=NAME:VALCookieCookie を設定できます。
DPIdiscardpathURI の PATH_INFO を破棄します。このフラグは、Apache 2.2.12 以降で利用可能です。
EENV環境変数の値を設定できます。値を設定する場合は [E=VAR:VAL]、値を削除する場合は [E=!VAL] とします。VAR は変数名、VAL は変数です。(例:[E=hoge:123])
FforbiddenHTTP 403(Forbidden:閲覧禁止)エラーを返します。以降のルールは実行しません。
Gclose[3] 404と410の違い
HTTP 410(Gone:消滅)エラーはHTTP 404(Not Found:未検出)エラーとよく似ています。410は二度と復活しない場合に使われ、404は単純に見つからない場合に使われます。
gone
HTTP 410(Gone:消滅)エラー [3] を返します。以降のルールは実行しません。
H=Content-handlerHandler実行するアプリケーションのハンドラを強制的に指定します。例えば、拡張子のないファイルをすべて php アプリケーションとして実行するには、以下のように記述します。
RewriteRule !\. - [H=application/x-httpd-php]
LlastURLの書き換え処理を終了します。以降のルールは実行しません。
Nnext書き換え処理を先頭から再実行します。その際にマッチングされる URL は最後に書き換えられた URL となります。無限ループを引き起こさないように注意して下さい。
NCnocase条件を評価する時、英字の大文字小文字を区別しません。
NEnoescape&% をエスケープせずそのままの文字列として扱います。
NSnosubreqサーバ内部で発生するサブリクエストには、書き換え処理を行いません。
Pproxy強制的に置換対象部を内部的にプロキシリクエストとして、プロキシモジュールを通して出力します。置換文字列は、http:// から始まるようなプロキシモジュールで扱える有効な URI である必要があります。無効な URI を指定した場合は、エラーが返されます。ただし、このフラグを使用する場合は、mod_proxy が組み込まれている必要があります。
PTpassthroughRewriteRule ディレクティブのターゲット(または、置換文字列)は、ファイルパスを想定していますが、代わりに URI として処理を行います。Alias、Redirect、ScriptAlias ディレクティブを利用する際に指定します。
QSAqsappend書き換え前の URL と置換文字列に、それぞれクエリ文字列(URL の ? 以降の文字列)が存在する場合、置換文字列の末尾に & と書き換え前の URL に指定されたクエリ文字列を追加します。本フラグを指定しない場合、クエリ文字列は置換文字列で上書きされます。
R[=code]redirect指定したURLにリダイレクトします。[R=301] のようにレスポンスコードを指定することもできます。指定できるレスポンスコードの範囲は 300 ~ 399 までとなります。それ以外のレスポンスコードを指定した場合は、置換文字列が破棄されます。レスポンスコードを指定しない場合は 302(Found) になります。また、temp(デフォルト値)、permanentseeother のシンボル名も指定できます。
S=numskip指定した数のルールをスキップします。例えば、[S=2] と指定すると、条件に一致した場合、以降のルールを 2 つスキップします。
T=MIME-typetypeターゲットファイルを強制的に指定した MIME-type にします。

本ディレクティブの処理順は、先頭行から条件に一致する行を探索していきます。条件に一致した場合は、先頭行に戻り、再度条件に一致する行を探索します。例えば、下記の書き方では、index1.html にアクセスすると index2.html に書き換わった後、再度探索を行い最終的に index3.html に書き換わります。

# RewriteEngine On | Off
RewriteEngine On

# index1.html → index2.html → index3.html
RewriteRule index1.html index2.html
RewriteRule index2.html index3.html
RewriteRule は条件に一致すると先頭行から再探索を行う

以下のような書き方では無限ループを引き起こし、500 internal server error が発生するため注意が必要です。特に RewriteRule では正規表現によるURL指定が可能であるため、意図せずに無限ループを引き起こすケースがあります。

# RewriteEngine On | Off
RewriteEngine On

# index1.html → index2.html → index1.html → ...
RewriteRule index1.html index2.html
RewriteRule index2.html index1.html
無限ループする場合は 500 internal server error が発生する

上記のように無限ループを防ぐためには、[L] フラグを設定すればよいかと思うかもしれません。しかし、以下の例では index1.html にアクセスしても最終的には index3.html に書き換わってしまいます。

# RewriteEngine On | Off
RewriteEngine On

# index1.html → index2.html に書き換わった時点で終了しない
RewriteRule index1.html index2.html [L]
RewriteRule index2.html index3.html [L]
最終的に index3.html に書き換わる

これは RewriteRule の機能を提供しているモジュール mod_rewrite の問題ですが .htaccess では [L] フラグが有効になりません。.htaccess の上位ファイルである httpd.conf では [L] フラグが正常に動作しますが .htaccess では再探索が優先されます。そのため、無限ループを防ぐには次章の "RewriteCond" を使用する方法が一般的です。

RewriteCond ディレクティブ

RewriteCond ディレクティブは、RewriteRule ディレクティブの書き換えルールの条件を定義できます。本ディレクティブは、RewriteRule ディレクティブの前に 1 つ以上設置できます。複数設置する場合は、オプションによって OR 条件にするか選択できます。オプションを省略した場合は、AND 条件として処理されます。本ディレクティブの条件を満たした場合、直後の RewriteRule ディレクティブの書き換えが行われます。条件を満たさない場合は、直後の RewriteRule ディレクティブが実行されないため、無限ループを防ぐ方法として有効です。

# Rewriteエンジンを起動
RewriteEngine On

# RewriteCond テスト文字列 条件 オプション
RewriteCond %{HTTP_HOST} ^(www\.)?murashun\.sakura\.ne\.jp$ [NC]

# RewriteRule 条件 置換文字列 [フラグ]
RewriteRule .* http://murashun.jp%{REQUEST_URI} [R=301,L]
sakura.ne.jp のドメインを murashun.jp に書き換える

RewriteRule と RewriteCond を組み合わせた処理順は、一般的な上から下に流れるプログラムとは異なります。基本的な処理順としては、以下になります。

  1. URL が 条件A にマッチし、かつ
  2. 文字列B が 条件C にマッチする場合
  3. URL を 文字列D に書き換える
RewriteCondの処理順
RewriteCondの処理順

プログラムで表現する場合は、以下になります。

if( URL =~ A && B =~ C){
  URL = D;
}
RewriteRule と RewriteCond の処理順

A は URL を書き換えるかの条件であるため、文字列 A を 文字列 D で書き換えるということではありません。

RewriteCond で使用できるパターンとオプションは以下のとおりです。

条件に利用できるパターン
パターン説明
!テスト文字列が条件と不一致である場合、true を返す
<テスト文字列を数値、または辞書順として大小比較を行い小さい場合、true を返す
>テスト文字列を数値、または辞書順として大小比較を行い大きい場合、true を返す
=テスト文字列を数値、または辞書順として等価比較を行い等しい場合、true を返す
-dテスト文字列のディレクトリが存在する場合、true を返す
-fテスト文字列のファイルが存在する場合、true を返す
-sテスト文字列のファイルが存在し、サイズが0byteでない場合、true を返す
-lテスト文字列がシンボリックリンクである場合、true を返す
-xテスト文字列に対して実行権限がある場合、true を返す
-Fテスト文字列に対してアクセス可能なパスである場合、true を返す
-Uテスト文字列に対してアクセス可能なURLである場合、true を返す
条件を評価する時のオプション
オプション説明
[OR]連続する RewriteCond のいずれかが true の場合に実行する。
省略時は、連続する RewriteCond がすべて true の場合に実行する。
[NC]条件を評価する時、大文字小文字を区別しない。

後方参照

後方参照とは、正規表現の "(" , ")" に囲まれた条件にマッチングした文字列を "後方" で "参照" できる方式です。以下の例は、マッチングした文字列を "$1" で参照しています。

# Rewriteエンジンを起動
RewriteEngine On

# URLからwwwは取り除くが、ディレクトリ名やファイル名は後方参照で使いまわす
RewriteCond %{HTTP_HOST} ^www\.murashun\.jp
RewriteRule ^(.*)$ https://murashun.jp/$1 [R=301,L]
後方参照を利用してURLを正規化する

RewriteRuleの後方参照

RewriteRule で後方参照を利用する場合は "$n" で表記します。( n には 0 ~ 9 の数字が入ります。) 後方参照は、複数の条件がある場合でも利用できます。マッチング条件が複数ある場合は、左側から $1, $2 というように、どの条件にマッチングした文字列を参照するか選択できます。ただし、$0$1 は同じ値を示します。また、10 を超える後方参照は使用できません。$10 と記述しても "$1""0" の文字列として認識されます。

# Rewriteエンジンを起動
RewriteEngine On

# /blog/foo/bar → /foo/bar に変換される
RewriteRule ^/blog/(.*)/(.*)$ /$1/$2
#                   $1   $2
RewriteRuleにおける後方参照

RewriteCondの後方参照

 RewriteCond で後方参照を利用する場合は "%n" で表記します。( n には 1 ~ 9 の数字が入ります。) 表記する記号が RewriteRule と異なり % であることと、%0 は使えないことに注意して下さい。その他の使い方は RewriteRule と同様です。

# Rewriteエンジンを起動
RewriteEngine On

#                                 %1   %2
RewriteCond %{REQUEST_FILENAME} ^(.*)/(.*)/index.html$
RewriteRule ^/blog/(.*)/(.*)$ /$1/%1/$2/%2
#                   $1   $2
RewriteCondにおける後方参照

リダイレクト関連のレシピ集

Apache はリクエストを受信すると指定されたドキュメントを提供しますが、別の場所にあるドキュメントを提供したい場合もあります。リダイレクトは、ドキュメントを新しい場所に移動しなくても、今ある場所に置いたまま提供できます。リダイレクト関連のレシピ集は、URL の正規化や、Web サイトが移転した場合、メンテナンスページの表示方法などを紹介します。

URL の正規化 - www の省略

URLは、以下の構造となっています。URLのホスト名の部分は、サーバによっては省略が可能です。さくらインターネットでは、www を省略しても同一の Web サイトが表示される仕様になっています。ただし、Google では同一ドメインでも www のあり・なしによって異なる Web サイトと認識します。そのため、両方の URL で同一コンテンツにアクセス可能な場合は URL の正規化を行って下さい。

http://www.murashun.jp/contents/index.html
--A--  -B- -----C----- ---D---- ----E-----

A : スキーム
B : ホスト名
C : ドメイン名
D : ディレクトリ名
E : ファイル名

www を省略するには以下のように記述します。以下の例では、環境変数の "HTTP_HOST" が www を含む URL の場合、www を省略した URL に書き換えてリダイレクトしています。$1 は、リクエストされたページの URL を後方参照で正規化済みの URL の末尾に加えています。

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.murashun\.jp
RewriteRule ^(.*)$ https://murashun.jp/$1 [R=301,L]
URL の www を省略する

URL の正規化 - http から https にリダイレクト

SSL 暗号化通信対応後、http から https にリダイレクトする設定例です。

RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
http から https にリダイレクトする例

 プロキシサーバを経由している場合は、特殊な環境変数 "X-Forwarded-Proto" を確認します。

RewriteEngine on
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
http から https にリダイレクトする例

 ただし、さくらインターネットの SNI SSL では、上記の設定でリダイレクトループが発生します。これは、さくらインターネットの HTTPS (SNI SSL) は、リバースプロキシとして動作しており、RewriteCond ディレクティブによる HTTPS 判定ができないためです。そのため、ユーザが HTTPS でアクセスしても、実行ファイルからは HTTP でアクセスしたものと見なされます。リダイレクトループを回避するには、以下の設定を試して下さい。

RewriteEngine On
RewriteCond %{HTTP:X-SAKURA-FORWARDED-FOR} ^$
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
http から https にリダイレクトする例 (さくらインターネットの場合)

 上記の設定でもリダイレクトループが発生する場合は、HSTS (HTTP Strict Transport Security) を設定して下さい。この設定は、Web サーバがブラウザに対して、次回以降の接続は HTTP の代わりに HTTPS を使うように通知するセキュリティ機構です。

Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
HSTSにより次回以降 HTTPS を通知する例

上記の設定は max-age で証明書の有効期間を 1 年間 (60 × 60 × 24 × 365 = 31536000) 設定しています。max-age 以降はオプションとなり省略可能です。また、オプションを設定する場合は ; で区切って設定します。

includeSubDomains は、すべてのサブドメインにも設定を適用するという意味です。サブドメインを持たない場合は、省略しても問題ありません。

preload は、HSTS の特性上 "初回は HTTP 接続になる" 脆弱性を回避するオプションです。具体的には HSTS の一覧に Web サイトのドメインを登録しておき、初回接続時から HTTPS 接続にするようにします。HSTS の一覧への登録は、以下の URL から受け付けています。ただし、登録が受理されても処理されるまでに数週間かかります。

Web サイト全体をリダイレクトする

Web サイトが新しいドメインに移転する場合は、Web サイト全体をリダイレクトします。例えば、以下の設定では oldsite.com/sample.html を newsite.com/sample.html にリダイレクトします。

# Web サイト全体をリダイレクトする設定例
Redirect 301 / http://newsite.com/
Web サイト全体をリダイレクトする設定例

上記の設定が書かれた .htaccess は oldsite.com のトップディレクトリに配置して下さい。

URL から拡張子を消す (clean URL)

一般的に URL にはファイルの拡張子が含まれますが、拡張子を消すことでクリーンな URL であるように見せることができます。下記の設定では、ファイル名の拡張子が .php の場合に省略する設定例です。

# .php を省略する設定例
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^([^.]+)$ $1.php [NC,L]
.php を省略する設定例

  http://example.com/page.php?id=123
  http://example.com/page?id=123

.php が省略された例

メンテナンスページにリダイレクトする

Web サイトを更新中は、どのページを閲覧してもメンテナンスページを表示するサイトがあります。例えば Apple はメンテナンス中には "We'll be back." と表示することが恒例になっています。

ただし、単純にメンテナンスページを用意して、そのページにリダイレクトするだけでは十分ではありません。メンテナンス中に検索エンジンのクローラーが訪れた場合、メンテナンスページにリダイレクトされますが、HTTP ステータスコードが 200 となるため、コンテンツとしてキャッシュしてしまいます。また、HTTP 404 (Not Found:未検出) を設定すると、クローラーはサイトが無くなったものと判断し、インデックスから削除される場合があります。そのため、HTTP ステータスコードは 503 (Service Unavailable:サービス利用不可) を返すのが正しい方法です。

# HTTP ステータスコード 503 を返す場合に表示するページ
ErrorDocument 503 /maintenance.html

RewriteEngine On
RewriteCond %{REQUEST_URI} !=/maintenance.html
RewriteRule ^.*$ - [R=503,L]

# メンテナンス終了時間を設定する
Header set Retry-After "Fri, 1 Jun 2016 6:00:00 GMT"
メンテナンスページにリダイレクトする例
Category:
プログラミング
公開日:
更新日:
Pageviews:
119
Shares:
2
Tag:
.htaccess
Apache
HTTP
Server
hatebu icon
hatebu