.htaccess による転送設定(リダイレクト)を設定する方法に関してです。ついつい何となくでやりくりしてしまう.htaccess における、mod_rewrite を使ったリダイレクトの正しい記述方法を、最新の Apache によるマニュアルを元に、まとめ直しました。
目次
【はじめに】.htaccess とリダイレクト
.htaccess とは?
「.htaccess(エイチティーティーアクセス)」とは、Apache(アパッチ)と呼ばれるWebサーバーにおける、ディレクトリごとに個別設定を行うためのファイルです。Apache サーバーにおけるリダイレクトの設定は、通常、任意のディレクトリにこの .htaccess を配置して、そこに転送処理を書き込みこむ事が一般的です。
ディレクトリ単位での設定に便利
Apache Webサーバーでは、サーバー全体に適応する設定は「httpd.conf」という設定ファイルに書き込むことになっているのですが、レンタルサーバー環境では、httpd.conf に直接書き込み権限がないケースがほとんどです。
もしくは、単に、頻繁にメインの設定ファイルを変更して、記述ミスによりエラーを起こして、サーバー全体を止めてしまうリスクを避けたい場合もあると思います。
そのような場合のために、「.htaccess」という設定ファイルが便利になります。.htaccess に記載された設定内容は、そのディレクトリとその配下のサブディレクトリに対するアクセスに対してのみ適応されますので、それ以外のディレクトリに影響を与えずに、リダイレクトなどの設定を行える、というわけです。
.htaccess とApache Web サーバー
Apache Webサーバーは、Webサーバーとしては非常に多くのシェアを有しています。例えば、Macbook に標準でインストールされている Webサーバーは Apache Webサーバーです。
また、国内のレンタルサーバーの多くも、ほぼ Apache Web サーバーが利用されていたのですが、近年はサーバー動作の高速化の目的で「Nginx(エンジンエックス)」と呼ばれる別のサーバーへの移行も多くなっています。一方で、互換性を保つため、.htaccess は継続して設定ファイルとして利用できるようサポートされているようです。利用者としては、サーバーの種類を気にしなくて良いので、安心ですね。
この記事でも、.htaccess に対してリダイレクト処理を記述する前提で進んで行きます。
.htaccess の作成方法
方法1) テキストエディタで作成する
.htaccess は、ただのテキストファイルですので、手持ちのテキストエディタを使って、.htaccess という名前のファイルを作成して、所望のディレクトリ(多くの場合、Webサイトのルートディレクトリ)に配置すればOKです。レンタルサーバー上での配置であれば、FTPなどが利用できるでしょう。
1点だけ注意が必要なのが「改行コード」です。.htaccess では。改行コードとして「LF(ラインフィード)」のみを認識します。まれに Mac で編集したファイルが「CR(キャリッジリターン)」を改行コードとして含む場合がありますので、改行コードを変更できるテキストエディタを利用するようにしましょう。
もしテキストエディタに、Vim を利用する場合は、こちらに改行コードについてまとめています。
方法2)レンタルサーバの管理画面から配置する
もしレンタルサーバー上で作業している場合は、レンタルサーバーの管理画面から .htaccess を編集できる場合も多いですので、管理パネル内を探してみてください。管理パネル上にテキストエディタが用意されていることもありますので、その場合は改行コードは心配しなくても良いでしょう。
リダイレクトの種類「内部転送」と「外部転送」
はじめの予備知識として、下記の2つのリダイレクトの種類を区別しておきましょう。
リダイレクトの種類
- 外部転送:アクセスを異なるドメインやURLパスに転送する。
- 内部転送:URLはそのままで、内部的に異なるディレクトリを割り当てる。
(1)は外部転送(External Forwarding)と呼ばれ、リクエストのURLを書き換えて、別のディレクトリや、完全に別のサイトに転送します。
一方で(2)は内部転送(Internal Forwarding)と呼ばれ、サーバー内で内部的にディレクトリの割当を変更する設定です。内部転送では、サーバーで内部的に割当が変更されるだけですので、ブラウザのアドレスバーのURLが書き換わることはありません。
.htaccess での、内部転送と外部転送の区別
.htaccess にリダイレクトを書き込む場合では、下記の条件の満たす場合は外部転送、そうでない場合はディレクトリの内部転送となります。(mod_rewrite を利用したリダイレクト設定の場合です)
「外部転送」となる条件
- リダイレクト設定の行末に、転送フラグ([R])を記述している。
- リダイレクト先の指定が、「http://」などのプロトコルで開始するURL、すなわち、外部サイトが指定されている
なお、外部のWebサイト(URLが異なる)への転送は、アドレスバーのURLを変更せずに転送することできません。(どうしもて実現したい場合、「リバースプロキシ」と呼ばれるしくみを実装をするなどで実現できますが、これは .htaccess の範疇ではありません。)
特定の意図がない場合は、通常のリダイレクトでは、外部転送することが多いと思うよ。末尾の R フラグを書き忘れなければ、問題ないね。
.htaccess の設定項目の詳細
それでは、.htaccess を使ったリダイレクト設定について詳しく見ていきましょう。まずは、サンプルをもとに、設定項目の詳細仕様を解説していきます。
ドメインが指すディレクトリのルートに配置された「img」ディレクトリ内へのアクセスで、画像ファイルのみを新しい「new-img」ディレクトリに転送します。
#
# リダイレクト設定のサンプル
#
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/img/.*(\.jpg|\.png)
RewriteRule ^img/(.+) new-img/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* /index.php [R=301,L]
</IfModule>
設定項目を詳しく見ていきます。
<IfModule mod_rewrite.c> … </IfModule>
まず、今回記述する設定内容は、全て<IfModule mod_rewrite.c></IfModule>という設定で囲む事で、文字通りですが、使っているWebサーバー上で「mod_rewrite」というモジュールが利用可能な場合のみ、このタグ内の設定値を読み込むようにできます。もし mod_rewrite が利用可能でなければ、今回記述する設定は全てサーバーエラーの原因となり、リダイレクト以前に、Webサーバー自体がそもそもうまく動作できなくなってしまう(500 Internal Server Error が返される)からです。
mod_rewrite は、レンタルサーバーであっても、おそらくほとんどのWebサーバーで利用可能なはずですが、まれに利用できない環境がありますので、注意して下さい。
RewriteEngine
mod_rewrite モジュールを利用する際に記載します。「On」と記載しましょう。
RewriteBase
こちらは .htaccess で最も仕様が混乱しやすい設定の一つです。
RewriteBase は、RewriteRule 設定で指定される「”リダイレクト先の” URLのベースディレクトリ」を設定します。(RewriteRule の「”リダイレクト元” のURL」や、サーバー変数 %{REQUEST_URI} の値には一切影響しません)
例えば、設定値をサンプルのように「RewriteBase /」と記載すれば、ベースディレクトリは、その .htaccess が配置されたディレクトリ自体となります。仮に「RewriteBase /htaccess-test/」と記載(*1)すれば、RewriteRuleでリダイレクト先に「index.php(最初のスラッシュがない事に注意して下さい)」が指定されれば、「/htaccess-test/index.php」が適応されます。(末尾のスラッシュ(Trailing slash)は書き忘れても自動的に補完されます。)
また、RewriteBase の値が利用されるのは「RewriteRule」に指定されたリダイレクト先URLが相対パスで開始する場合のみです。すなわち、もしRewriteRuleでリダイレクト先に指定した文字列が、「/index.php」 であったり、「http://googole.com」であれば、RewriteBase の値は使われる事がありません。
RewriteBase と RewriteRule の関係はなんだか紛らわしい・・・。RewriteRule に長いパスを何度も繰り返し記述するのが面倒な時だけ、 RewriteBase を使えばよさそうだね。
RewriteCond
RewriteCond は、自身の直後の RewriteRule を適応する条件を記載します。多くの場合、サーバー変数や mod_rewrite 特有の変数から得られる文字列を対象とし、正規表現やアパッチ特有の条件記号を用いて、マッチングを行います。
よく利用するサーバー変数
サーバー変数は「%{変数名}」として参照します。下記は、.htaccess で利用される代表的なサーバー変数です:
変数名 | 取得できる値 |
---|---|
HTTP_HOST | ホスト名、すなわちドメイン。(例:www.example.com) |
REMOTE_ADDR | ホストのIPアドレス。 |
REQUEST_URI | リクエストにおけるURI、すなわち、ファイルパス。「/index.php」のように、最初にスラッシュが含まれる点に注意する。また、クエリ文字列は含まれない |
REQUEST_FILENAME | ファイルシステム上のファイルパス。(例:/var/www/html/my-site/about.php)ただし、リクエスト時点でファイルが断定できていなければ、REQUEST_URI と同じ値が返される。 |
HTTPS | もしリクエストプロトコルが「https」であれば"ON"、そうでなければ"OFF"。 |
特に「REQUEST_URI」は注意が必要です。
PHPでのサーバー変数$_SERVE[‘REQUESET_URI’] は、クエリ文字列を含みますが、アパッチの %{REQUEST_URI} は、クエリ文字列を含みません。クエリ文字列は別途 %{QUERY_STRING} を使って参照することになります。
REQUEST_URI
The path component of the requested URI, such as “/index.html”. This notably excludes the query string which is available as its own variable named QUERY_STRING.
便利な条件記号
利用できる条件記号の一部で、最もよく利用するものです。下記は、サーバー変数 %{REQUEST_URI} と共に使うことがおおいでしょう。
記号 | 意味 |
---|---|
-d | 対象文字列をパスとみなし、その存在とディレクトリかどうかをテストする。 |
-f | 対象文字列をパスとみなし、その存在とファイルかどうかをテストする。 |
-F | 対象文字列をパスとみなし、そのファイルが存在し、サーバーシステムからアクセス可能な状態かどうか。(副次的なリクエストによりチェックするため、パフォーマンスへの影響がある点に注意) |
-s | 対象文字列をパスとみなし、そのファイルが存在し、ファイルサイズがゼロ以上かどうか。 |
-x | 対象文字列をパスとみなし、そのファイルが存在し、実行権限( executable )があるかどうか。 |
また、複数の条件を連続記載することもできます。デフォルトでは条件は論理和(AND)で制限が強まってしまいますが、RewriteCondの末尾に[OR]フラグを記載することで、(OR)条件を記載することができます。
さらに、条件の冒頭に「!」マークをつけることで、条件の真偽を逆転することができます。これは便利なので利用する機会が多いと思います。
ひとつか複数の RewriteCond 設定行のあとに、ひとたび RewriteRule 設定行が現れれば、それまでの条件の蓄積は一度クリアされます。
すべての利用可能なサーバー変数と、条件記号に関しては、アパッチドキュメントをご覧ください:
RewriteRule
さて、こちらが実際のリダイレクト処理を設定する設定です。サンプルの「^img/(.*)$」などは、いわゆる正規表現によるパターンの記述が必要なのですが、「正規表現?よくわからない、詳しく知りたい!」という方は、こちらの記事をご一読下さい。
RewriteRule のマッチングの対象は何?
この仕様はしばしば混乱のもとになるのですが、RewriteRule におけるマッチング対象は、.htaccess 内においては「リクエストURLのパスの部分から、.htaccess が置いてあるディレクトリまでのパス(最後のスラッシュまで)切り取ったもの」と定義されています。(ただし、http.conf 内で設定する場合では仕様が異なります。あくまで.htaccess による設定の場合です。参考:アパッチドキュメント)
例えば、「http://example.com/sub/img/profile.jpg」のリクエストで、「sub」ディレクトリに .htaccess が配置してあれば、マッチング対象は「img/profile.jpg」となります。サーバー変数 %{REQUEST_URI} と定義が異なりますので、注意が必要です。
RewriteRule のリダイレクト先の指定
リダイレクト先の指定では、リダイレクト先となるURL文字列を記載します。
サンプルのように、リダイレクト先を相対パス(スラッシュで開始しない、ファイルパス)で記載すれば、RewriteBase の設定値をパスとして利用できます。もし、RewriteBase 設定行が存在しなかったり、その値を使いたくなければ、リダイレクト先の指定を「/xxx,jpg」などとスラッシュで書き始めればOKです。
[R=301]
こちらは「フラグ」と呼ばれる、RewriteRule の設定行に追加できるオプションで、これにより実際にリダイレクトが発生します。基本的には、[R=301]、[R=302]のどちらかを設定します。( [R] と省略すれば [R=302] と同義です。)
これら2種類の数字の違いは、下記のようなものです。
- 301:恒久的なリダイレクト。検索エンジンが引っ越しと認識する
- 302:一次的なリダイレクト。検索エンジンが(すぐには)引っ越しと認識しない
これらの数字はHTTPステータスコードと呼ばれ、「アクセスされたWebページの状態」を発信する大切な情報です。通常のリダイレクトでは 301 を利用し、なにか特別な理由で「一時的に」リダイレクト処理を行うのであれば、302 を利用します。
こちらの記事に詳しいHTTPステータスコードの解説のセクションがありますので、ご一読下さい。
[L]
このフラグがあれば、この行でRewriteRule にマッチした場合、次の行に進むことまずに、その時点で処理を停止します。逆に、このフラグを忘れていて、次の行に別の RewriteRule 設定がまた記載されていれば、せっかくマッチしたリダイレクト先のURLが再度書き換えれてしまいます。
また、上記の [R] フラグを利用するときは、必ず[L] を併記する必要があります(そうでない場合、エラーが発生していまいます)結果的に、RewriteRule を記載した行は、[R=301,L] と行末に記述することになるはずです。
いろいろあって、結構難しいね・・・。でも、仕様を意識しながら書いたほうが、正しいリダイレクトを実現する近道かもしれないね。
.htaccess リダイレクトの書き方、サンプル集
さて、ここからは、実際の .htaccess への設定のサンプルを見ていきます。mod_rewrite を利用する前提で進みますので、ご利用中のサーバーで mod_rewrite が利用できる事をご確認下さい。
別のWebサイトへリダイレクトする設定(ディレクトリ構造を維持 ≒ ドメインのみ変更)
Webサイトへのアクセスを、別のWebサイトへ(=新しいドメイン)に変更します。リダイレクト先URLの「$1」が、マッチした文字列を再利用している事に留意して下さい。
上記での解説の通り、RewriteRule 設定行でキャプチャされる文字列は、URLパスのスラッシュの後の部分ですので、ご注意くださいね。(例:http://example.com/page-a の”page-a”部分)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule (.*) http://example.com/$1 [R=301,L]
</IfModule>
最後に、RewriteRule 行の末尾の[R=301]により、HTTPステータスコードを指定しています。
サイトの引っ越しでは、301 (Permanently Moved)シグナルを送ることは、SEO観点で重要です。こちらの記事にHTTPステータスコードについて詳しく言及しています。
また、サイトの引っ越しではサイト内のすべての(もしくは、主要な)ページを、すべて新しいドメイン配下の対応する個々のページに転送する事が推奨されています。そうすることで、検索エンジンによるページの評価を新しいドメインに引き継ぐ事ができるのです。
別のWebサイトへリダイレクトする(すべて同じページに転送)
すべてのアクセスを、任意のページに転送します。下記は新しいドメインのトップページに誘導しています。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* http://example.com [R=301,L]
</IfModule>
ちなみにSEO観点ですが、、「Webサイトの引っ越し」においては、単一ページへのリダイレクトでは、過去のページがもっていた評価を引き継ぐことができません。対応するページがある場合は、できる限り先述のページごとの個別転送を行うと良いでしょう。
ドメインの www の有/無の統一する
ドメインについている「www」の有・無を統一します。これは上記のサイトドメインの変更と同じ記法ですが、RewriteCond という設定項目を追加していることに注意して下さい。
RewriteCondで、リダイレクトする条件をwww 有(無)と制限することで、htaccess 内での処理が無限ループに陥るの防いでいます。このように、アクセスが .htaccess により外部転送された後、改めて .htaccess の処理を通されることを意識しながら、設定を書き込む必要があります。
www無のドメインに統一する
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.com
RewriteRule ^(.*) http://example.com/$1 [R=301,L]
</IfModule>
www有のドメインに統一する
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com
RewriteRule ^(.*) http://www.example.com/$1 [R=301,L]
</IfModule>
SEO観点では、このような明示的なリダイレクト設定が行われている場合、検索エンジンは、転送先のURLを検索結果に優先的に表示するようになります。このように複数のドメインでアクセス可能なWebサイトのURLを統一することを、「URLの正規化」と呼びます。
サブドメインURLを、サブディレクトリURLにリダイレクトする
サブディレクトリ型のURLへの変更例です。下記の例は、「sub.example.com」を「example.com/sub」へ転送しています。このようなWebサイトの引っ越しを行うこともあるでしょう。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^sub\.example\.com
RewriteRule ^(.*) http://example.com/sub/$1 [R=301,L]
</IfModule>
サブディレクトリは、サブドメインよりもSEO上で有利だと言われいてます。
検索エンジンは、サブドメインは、主ドメインとは完全に別のWebサイトとして扱います。属性の近いコンテンツであれば、別々のサイトに分散させるよりも、1つの同じドメインの下に束ねたほうが、主ドメインの評価の向上につながります。
http をリダイレクトし、アドレスを https に統一する
ドメインを SSL化した後に、SSL保護アドレス(https:// で開始するURL)に統一します。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*) https://%{HTTP_HOST}/$1 [R=301,L]
</IfModule>
まれにサーバー変数 %{HTTPS} に値が入っていないサーバー環境もありますので、注意して下さい。さくらレンタルサーバーは過去にそのような振る舞いで、ハマる人が続出した時期がありまたが、現在(2018年4月)サーバー仕様の変更により解決しているようです。
なお、上記のリダイレクト先「/$1」 は、サーバー変数「%{REQUEST_URI}」と一致しますので、どちらでも構いません。
ファイル単位でリダイレクトする
特定のファイルへのアクセスを、別のファイルへの転送します。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^services/old-file.php /services/new-file.php [R=301,L]
</IfModule>
前述の通り、.htaccess上では、リダイレクト元のパターン指定は、ホスト名直後のスラッシュを含まないURLパス、逆に、リダイレクト先はホスト名直後のスラッシュを含むパスとなります。これはApache の仕様で定義されていますが、混乱しやすいため、注意が必要です。
参考:RewriteRule directive: What is matched?
特定の種類のファイルのみリダイレクト
img ディレクト内で、拡張子が「.jpg」か「.png」の場合だけリダイレクトします。RewriteCondで、拡張子を条件に、直後のRewriteRuleが適応されます。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} ^.*\.jpg|\.png
RewriteRule ^img/(.+) /new-img/$1 [R=301,L]
</IfModule>
ディレクトリの内部転送(URL はそのまま)
ブラウザーのURLの変更すること無く、内部的にディレクトリ割り当てを変更します。これは、内部転送(Internal Forwarding)と呼ばれます。いわゆるリダイレクトとな異なる処理となりますので、注意して下さい。
下記は、/original ディレクトリ配下へのアクセスを、パスを維持しながら /new ディレクトリへ割り振る処理です。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^original/(.*) /some-other-dir/new/$1 [L]
</IfModule>
内部転送となる条件は、行末に[R]オプションが書かれていない、かつ、転送先指定が http・・など外部サイト指定ではない、という条件でした。
ディレクトリを外部転送
こちらは、ブラウザーのURLが変更する、いわゆるリダイレクトです。行末に [R=301] のオプションが設定されていることに注意して下さい。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^original/(.*) /some-other-dir/new/$1 [R=301,L]
</IfModule>
【参考】WordPress デフォルトの .htaccess
参考まで、WordPress のデフォルトの.htaceess を見てみましょう。
#
# WordPress デフォルトの .htaccess
#
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
条件記号「-f」「-d」で、ファイルやディレクトリへの直接のアクセスのみ通し、それ以外のパスへのアクセスをすべて /index.php に送っています。ここまで、記事をしっかりと読んで頂いた方はRewriteRuleが「内部転送」と鳴っている点に気がつくと思います。
WordPress は、ページアドレスを読み取って、対応するページを表示する仕組みをそのシステム内に持っています。リクエストのアドレスを維持したま、index.php を読み込み、PHPで作られたシステムがリクエストをハンドルします。結果のとして、対応するページが動的に生成され、それがブラウザに表示される、というわけです。
蛇足ですが、上記の RewriteBase の値は全く参照されません。理由は RewriteBase のセクションで述べたとおりです。WordPreses のインストール時にデフォルトで生成される .htaccess ですので、そのまま例として利用しています。
なるほど、よく分かっていなかったWordPress の .htaccess が、ようやく理解できたよ・・・。
.htaccess 設定のトラブルシューティング
.htaccess のファイルパーミッションは?
特殊な設定ファイルなので、ファイルパーミションでApache実行ユーザ(www、 _www などという名前が多いですが、環境依存です)にREAD権限がないと、当然ですが設定が適応されません。
ファイルの文字コード設定は適切?
よくあるのですが、ググった情報をブラウザからそのまま貼り付けて保存してしまうと、何故かうまくいかない場合があります。
そのような場合、文字コードに異常が発生していることを疑ってみて下さい。改行コードが不正なものだったり、画面上に表示されない文字コードが紛れ込んでエラーを起こしていることがあります。
vim が利用できる場合は、「vim -b」として、バイナリモードでファイルを開くと、不要なバイナリももれなく表示してくれますので、確認に重宝します。
すこし技術的ですが、改行コードのずれについてはこちらもご一読下さい:
ブラウザーによるDNSキャッシュに注意!
例えば、Google Chrome を始めとするブラウザーは301コードの転送が成功すると、リダイレクト先のURLをブラウザ上でキャッシュしてしまいます。つまり、.htaccess で設定内容を書き換えても、一度成功した301転送先URLに(勝手に!)直接接続され続けてしまいます。
普段は表示速度を早めてくれているのですが、開発に置いてはちょっとしたおせっかいですね。
これを迂回するため、工夫としては、.htaccess の設定内容をテストしている時は、あえて、R=302と設定してデバッグを行うと、手軽にこのキャッシュ機能を迂回できます。
なお、301キャッシュの削除に関しては、こちらにも詳しくまとめていますので、ご一読下さい。
必ず、ログを吐きながらテストしよう!
.htaccess でのリダイレクト設定では、正規表現を取り扱ったり、条件分岐が発生したりと、設定が複雑になりがちです。でも、設定項目がうまくいかない場合、何が間違っていたのかが明確に判断でないと、解決できずに深くはまり込んでしまうことが非常に多いです。Apache のエラーログは簡単に出力することができますので、テストする場合は必ずログを履きながらテストしましょう。
ログを出力する設定
Apacheの設定ファイルであれば、httpd.conf を開けば、 ある「ErrorLog」「LogLevel」の値がすぐに見つかるはずです。下記はMacOSにインストールしたApache のhttpd.conf の例です。
mod_rewrite が使えない場合・・・
最近はレアだと思いますが、古いサーバー環境では、mod_rewrite が利用できないケースもあります。このような場合、シンプルなリダイレクトであれば、Mod_rewriteなしでも設定可能です。
Redirect 301 index.php /new-dir/index.php
.htaccess リダイレクトの書き方に関する参考情報
Apache のマニュアルは英語なので、苦手な方は大変ですが、今回解説した仕様はすべて網羅されています。より複雑な設定のため、詳しい情報が必要なかたは、ぜひ読み込んで見て下さい。
今も、ほとんどのレンタルサーバで .htaccess が使えるんだね。