備忘録

【.htaccess】URI統一(www→なし)(http→https)(/index.html→/)

コード全体

<IfModule mod_rewrite.c>
  RewriteEngine on

  RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
  RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

  RewriteCond %{HTTPS} off
  RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

  RewriteCond %{THE_REQUEST} ^.*/index\.(html|php)
  RewriteRule ^(.*)index\.(html|php)$ https://%{HTTP_HOST}/$1 [R=301,L]
</IfModule>

解説

まずはこの記述

<IfModule mod_rewrite.c>
  RewriteEngine on

  #この中に色々書く

</IfModule>

1行目でApacheのmod_rewriteモジュールを読み込みます。

読み込めた場合は、そこから</IfModule>までが処理されることになります。

2行目のRewriteEngine onは、「今からリダイレクト処理を記述しますよー」の宣言です。

無くても大丈夫な場合もある?みたいですが、とりあえず記述しておくに越したことは無いはず。

以上で準備は完了です。

それでは、「#この中に色々書く」の部分にリダイレクトの処理を記述していきます。

(ちなみに.htaccessのコメントアウトは先頭に#)

wwwなしに統一

RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

RewriteCondの行の解説

RewriteCondにはRewriteRuleを実行する条件を指定します。

第1引数と第2引数の条件が一致しなければ、リダイレクトは行われません。

%{HTTP_HOST}はドメインを表す変数です。

このブログなら、「prog.morisakimikiya.com」になります。

^www\.(.*)$はwww.で始まる文字列を表す正規表現です。

  • ^が文字列の先頭
  • \が.のエスケープ
  • .*で任意の文字列(任意の1文字の0回以上の繰り返しなので)
  • それを囲む()は変数として切り出して後で使用するためのもの
  • $が文字列の末尾

第3引数の[NC]はオプションのフラグで、この場合、大文字小文字を区別しないことを指示します(no caseの頭文字)。

RewriteRuleの行の解説

第1引数にはリダイレクト条件のパスを正規表現で指定します。

ここでは^(.*)$と記述し、全てのパターンに一致するようにしています。

これにより、先ほどRewriteCondで絞ったwwwアドレス全てが置換対象となります。

なお()でこの文字列も取り出しています。

そして第2引数のhttps://%1/$1にリダイレクトします。

%NはRewriteCondで取り出したN番目の文字列、$NはRewriteRuleで取り出したN番目の文字列を指します。

最後に第3引数でリダイレクトの種類を指定しています。

R=301は301リダイレクトを指示しています。

またLはLastの頭文字で、ここで処理を終えてリダイレクトを行うことを示しています。

httpsに統一

RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

RewriteCondの行の解説

2つ目なので、簡単にいきます。

%{HTTPS}はURIスキームがhttpsであるか否かを表す変数です。

offまたは!onでhttps通信でない場合に変換を行います。

RewriteRuleの行の解説

先ほどと同様に^(.*)$で全てのパターンにおいて書き換えを行います。

今回は%{HTTP_HOST}でドメインを、%{REQUEST_URI}で現在のパスを指定しています。

結果的にhttpからhttpsへの統一となります。

***/index.(html|php)を省略して***/に統一

RewriteCond %{THE_REQUEST} ^.*/index\.(html|php)
RewriteRule ^(.*)index\.(html|php)$ https://%{HTTP_HOST}/$1 [R=301,L]

RewriteCondの行の解説

%{THE_REQUEST}はリクエスト文字列です。

なのでクエリなどを含む場合もありますが、その場合は今回の置換の対象外としています。

なお(html|php)は、hemlまたはphpを意味する正規表現の記法になります。

RewriteRuleの行の解説

index.html(index.php)より前の部分を後方参照したいので、第1引数はこれまでの^(.*)$ではなく、このような書き方をしています。

先ほどと同様にhttps://の後に%{HTTP_HOST}でドメインを指定し、第1引数から切り出したindex.html(index.php)より前の部分を繋げています。

これでスラッシュ止めのURIに統一されます。

Lフラグについて

上記の例では、各RewriteRuleに対してLフラグを付けています。

そのため、http://www.example.com/index.htmlのようなURIがあった場合、1つ目のルールが適用された時点でリダイレクトを行い、そのURIに対して再びチェックが行われることになります。

(別ディレクトリへ飛んで、他の.htaccessが適用されるような記述をした場合などは別ですが。)

RewriteRuleはそれまでに別のRewriteRuleが適用されていた場合、適用後のURIを対象にチェックを行うため、実はLフラグは必要ありません。

実際の処理にはほとんど差はありませんが、無駄を省くためにはラスト以外のLフラグは取り除いてしまっても良いと思います。

と思っていたのですが、WordPressサイトでこれを行うと、http:スキームの404エラーを吐くようなURIがhttpsの404にリダイレクトされなくなってしまいました。

WordPress無関係のサイトでは問題なかったので、WordPressの何か設定と干渉してしまっているのだと思います。

Lフラグを付けていれば問題は発生しないので、とりあえずこれで様子を見てみることにします。

エラーが出る場合

.htaccessファイルは最後に改行が必要です。

入れ忘れが無いか、注意してくださいね。

追記

偶然こんな記事を見つけました。

RewriteRuleの条件の中で、RewriteCondの比較を行う順番だそうです。

この記事内のリダイレクトではこの違いで結果に影響は出ないと思いますが、注意しておかないとハマることがあるかもしれませんね。

参考

RewriteCondが効かない?特定のディレクトリを除外する方法でつまずいたお話