.htaccess

目次

.htaccess とは?

.htaccess ファイル(分散設定ファイル)は、サーバの挙動を決定する設定ファイルのひとつで、ウェブサーバとして「Apache」が使用されていて、サーバ管理者が「.htaccess」ファイルの設置を許可していれば使用可能です。

通常 Apache は、httpd.conf というテキストファイルにディレクティブ(命令、コマンドのこと)と呼ばれる専用の命令を記述することで設定しますが、httpd.conf はサーバ管理者しか編集できません。それに対して、.htaccess ファイルは各ユーザが、各ディレクトリ単位で設定することができます。

.htaccess ファイルを設置すると、アクセス制限やユーザー認証、リダイレクト(自動転送)、404エラーページのカスタマイズなどを設定することができます。

.htaccess ファイルは単なるテキストファイルですが、以下の点に注意が必要です。

  • FTP の転送モード:「ASCII 転送」モード、または「テキストモード」でアップロードします。
  • パーミッション:可能であれば「604(グループ不可)」、またはデフォルトの「644」にします。使用しているプロバイダの設定に依存します。
  • .htaccess ファイルは設置されたディレクトリとその配下にある全てのディレクトリに影響を与えます。ルート(public_html)に.htaccess ファイルを設置した場合は、全てのフォルダに影響を与えます。
  • 必ず最後に改行(空白行)を入れます。(一行以上の空白も大丈夫)。
  • テキスト形式(プレーンテキスト)で保存します。UTF-8 として保存する場合は、BOMは付加しないようにします。
  • コメントは、行頭に「#」をつけます。行の途中よりコメントアウトする場合はダブルクォートで囲みます。”# comment”
  • .htaccess ファイルに文法誤りがあると、そのディレクトリ以下のファイルにアクセスしようとすると、500 Internal Server Error が発生します。
  • サーバ管理者が許可していない命令(ディレクティブ)を使うと、文法誤りのときと同様に 500 Internal Server Error が発生します。

セキュリティ関連の設定

.htaccess ファイルを使ってファイル一覧を表示させないようにしたり、特定のファイルへのアクセスを制限したりすることができます。

ディレクトリの内容を見せない

URI にスラッシュ(/) で終わるアクセスがあった際、index.html などのデフォルトで表示されるファイル(インデックスファイル)がない場合、以下のようなディレクトリのファイル一覧が表示されてしまいます(Apache の機能)。

ディレクトリのファイル一覧の表示例

各ディレクトリ内に index.html などのインデックスファイルを用意すれば良いのですが、すべてのディレクトリにそうするのは大変な場合は .htaccess を使って、すべてのディレクトリに対してファイル一覧を見せないようにすることができます。

以下は、ディレクトリのファイル一覧を見せないようにする設定です。

#ファイル一覧機能を無効にする命令
Options -Indexes

Options ディレクティブは、どのサーバー機能が特定のディレクトリで使用可能かを制御します。+ は既存の設定に追加し、- は既存の設定から削除します。(Apache コア機能:Options ディレクティブ

上位ディレクトリでの Options 設定は原則として下位ディレクトリまで適用されますが、プラス記号(+)によって先に無効にされていたオプションを有効にすることができます。逆にマイナス記号(-)は有効だった設定を無効にします。記号省略時は、上位ディレクトリでの Options 設定を継承せず、新規に Options 設定が適用されます。

Options ディレクティブに指定できる値
説明
All MultiViews を除いた全ての機能が有効となります。 (デフォルト)
ExecCGI mod_cgi による CGI スクリプトの実行を許可します。
FollowSymLinks このディレクトリ内でシンボリックリンクをたどれるようにします(シンボリック先へのアクセスも許可します)。
Includes mod_include が提供する SSI を有効にします。
IncludesNOEXEC SSI は有効になりますが、#exec コマンド と #exec CGI は無効になります。
Indexes もし、URL がディレクトリにマップするリクエストであって、 且つ DirectoryIndex で指定したファイル (例えば、index.html) が ディレクトリ内に無ければ、mod_autoindex が ディレクトリ内の一覧を整形して返します。
MultiViews mod_negotiation による コンテントネゴシエーション された "MultiViews" を許可します(言語ネゴシエーション機能を有効にします)。Options All が指定されていても MultiViews 機能は有効にならないので、明示的に記述する必要があります。
SymLinksIfOwnerMatch シンボリック先のファイルまたはディレクトリが、 シンボリックリンクの所有ユーザ ID と同じ場合にのみシンボリックリンクを たどれるようにします。
None 機能は全て無効になります。

前述の設定が有効になると、ブラウザーでアクセスした際に index.html などのインデックスファイルがない場合、以下のように 403 Forbidden エラーを返します。

ファイル一覧機能を無効にした場合の表示例

レンタルサーバによっては上記の設定をするとエラーになる(Options が使えない)場合があります。その場合は以下のように記述すると、ファイルの一覧機能は有効なままですがディレクトリの一覧は表示させないようにすることができます。

#ディレクトリの一覧を表示しない命令
IndexIgnore *

IndexIgnore ディレクティブは、ディレクトリの一覧を表示させる前に、非表示にするファイルを設定します。Webサーバーは、これらのパラメータと一致するファイルをサーバーが生成するディレクトリ一覧から除外します。

上記の設定が有効になると、以下のようにディレクトリの一覧を表示しません。

ファイルの一覧機能は有効なままでディレクトリの一覧は表示させない場合の表示例

ディレクトリの一覧ではなく任意のエラーメッセージを表示する

ディレクトリにインデックスファイルがない場合は、ディレクトリの一覧ではなく任意のエラーメッセージを表示するように設定することもできます。

以下のようなエラーメッセージの HTML ファイル(例 errmsg.html:ファイル名と内容は任意)を作成して、ルートディレクトリに配置します。もし画像やリンクなどを記述する場合は、どの階層で呼び出されるかわからないため「/」から始まるルートパス(トップディレクトリからのパス)または絶対パス(URL)で指定します。

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Forbidden - ページを表示できません -</title>
</head>
<body>
  <h1>Forbidden - ページを表示できません -</h1>
  <p>ファイル名を指定して接続してください。</p>
  <p>You don't have permission to access the requested resource. </p>
  <p>Please specify the page.</p>
</body>
</html>

.htaccess ファイルに以下を記述してインデックスファイルがない場合はエラーメッセージ(errmsg.html)を表示するように設定します。配置する場所は、全てのディレクトリに適用する場合は、ルートディレクトリに、特定のディレクトリに対して適用したい場合は、エラーメッセージを表示するようにしたいディレクトリに配置します。

DirectoryIndex index.html /errmsg.html

エラーメッセージのファイル(errmsg.html)を特定の場所に保存するには、上記のスラッシュ(/)以下にそのフォルダへのパスを記述します。以下は sample フォルダにファイルを配置する例です。

DirectoryIndex index.html /sample/errmsg.html

上記の設定が有効になると、以下のようにエラーメッセージを表示します。

インデックスファイルがない場合はエラーメッセージ(errmsg.html)を表示する例

DirectoryIndex

前述の内容と関連のある DirectoryIndex に関して。

URI をスラッシュ(/) で終わるアクセスがあると、通常はそのディレクトリにある index.html が呼び出されます。DirectoryIndex を設定すると、デフォルトのファイル(index.html)を異なるファイルに変更することができます。

DirectoryIndex ディレクティブは、ユーザーがディレクトリ名の最後にスラッシュ(/) を指定した場合に返すインデックスファイル(index.html、index.php、top.html 等)を設定できます。複数のリインデックスファイルを指定している場合は、先に書かれたものから優先して探します。

DirectoryIndex index.html index.htm index.php index.cgi

上記の場合、スラッシュ(/)で終わる URI にアクセスがあった場合、サーバは index.html がなければ index.htm を、index.htm がなければ index.php を、index.php がなければ index.cgi を順に呼び出すという設定です。

もし全て見つからなければ、Options Indexes でディレクトリ一覧の表示が許可されていれば、ディレクトリのファイルリストを表示し、許可されていなければ 、403 Forbidden を返します。

ファイルへのアクセス制御

<Files> や <FilesMatch> ディレクティブを使って、特定のファイルをブラウザーからアクセスできないよう制限することができます。

Files ディレクティブ

Files ディレクティブは <Files filename> ~ </Files> で囲まれたブロック内で記述した設定を、指定したファイルにのみ適用します。filename 引数は、ファイル名かワイルドカード文字列で、ワイルドカードでは ? は一つの文字、* は任意の文字列にマッチします。

以下は phpinfo.php への全てのアクセスを拒否する例です。

<Files phpinfo.php>
  Deny from all
</Files>

前述の例は、全てのアクセスを拒否する設定ですが、細かくアクセスを制限することもできます。

以下は特定の IP(192.168.1.*) 以外からの phpinfo.php へのアクセスを拒否する例です。

<Files phpinfo.php>
  Order Deny,Allow
  Deny from all
  Allow from 192.168.1
</Files>

Order では次の行に続く Deny(拒否) と Allow(許可) の指示の評価の順序を定義します。上記の場合、Deny を先に評価し、次に Allow を評価します。まず Deny from all で「全てのアクセスを拒否する」を評価し、次に、Allow from 192.168.1 を評価するので、特定の IP アドレス(192.168.1.*) のみを許可することになります。

<Files ~ "正規表現">

~ という文字を付加することで拡張正規表現を使うこともできます。この場合、<FilesMatch> と同じ意味になりますが、<FilesMatch> を使う方が 推奨されています。

以下は、.htaccess と .htpasswd を見れないようにする例です。

<Files ~ "^\.(htaccess|htpasswd)$">
  deny from all
</Files>

FilesMatch ディレクティブ

FilesMatch ディレクティブは、<FilesMatch regex> ~ </FilesMatch> で囲まれたブロック内で記述した設定を、正規表現(regex)にマッチするファイル名に適用させることができます。regex 引数は、正規表現を指定します。

以下は前述の例を FilesMatch ディレクティブを使って書き換えた例です。

<FilesMatch "^\.(htaccess|htpasswd)$">
  deny from all
</FilesMatch> 

ディレクトリへのアクセス制限

特定のディレクトリへのアクセス制限を行うには deny from all とだけ記述した .htaccess ファイルを指定のディレクトリに配置します。

CGI プログラムのみが利用するディレクトリ等では、HTTP 経由でのアクセスは必要ないので、以下を記述した .htaccess ファイルをそのディレクトリに配置すると、全てのアクセスを拒否することができます。

deny from all
# 最終行に空行を含めることを忘れないように
          

ベーシック(Basic)認証

ベーシック認証には .htaccess ファイルの他に、ユーザ名(ID)とパスワードを記述する .htpasswd ファイルを用意する必要があります。

特に指定がなければ、ベーシック認証を記述した .htaccess ファイルを配置したディレクトリ配下の全てのファイルが認証対象となります。

.htaccess の記述例

AuthType Basic
AuthUserFile /home/xxxxx/.htpasswd
AuthGroupFile /dev/null
AuthName "Login Password Required"
Require valid-user        
  • AuthType:ユーザ認証システムの種別(Basic)を指定します。指定できる値は、Basic(Basic 認証)または Digest(MD5 認証)です。
  • AuthUserFile:認証のためのパスワードファイル(.htpasswd)の場所を指定します。値には、サーバのルートディレクトリからのフルパス(絶対パス)を指定します。
  • AuthGroupFile:ユーザ認証のためのグループファイルを指定します。グループファイルは Require ディレクティブで Require group を宣言した場合のみ必要になります。利用しない場合は、この例のように「/dev/null」を指定します。
  • AuthName:ファイルへのアクセス時にダイアログボックスに表示する任意の文字列を指定します。
  • Require:ユーザ認証の際に、認証させるユーザを指定します。valid-user は、ID とパスワードが正しければ、パスワードファイル(.htpasswd)に記述されている全てのユーザを許可することを示しています。個別に指定する場合には、アクセスを許可するユーザー名やグループ名をスペースで区切って指定します。

特定のファイルにのみベーシック認証を設定するには Files ディレクティブを使ってファイル名を指定します。以下は、wp-login.php にベーシック認証を設定する例です。

<Files wp-login.php>
  AuthType Basic
  AuthUserFile /home/xxxxx/.htpasswd
  AuthGroupFile /dev/null
  AuthName "Login Password Required"
  Require valid-user        
</Files>   

.htpasswd の作成

.htpasswd ファイルに記述する情報は、ユーザー名(ID)とパスワードで、1行に1アカウントずつ記述します。ユーザー名(ID)とパスワードは「:(コロン)」で区切ります。また、パスワードについては、暗号化(ハッシュ化)して保存する必要があります。

ユーザー名:暗号化されたパスワード

パスワードの暗号化は PHP などのスクリプトや何らかのツールを使って行う必要があります。PHP での暗号化は「ユーザー名とパスワードをハッシュ化」を参照ください。以下のパスワード生成フォームでは、パスワードは PHP の crypt() 関数を使って暗号化(ハッシュ化)しています。

$pwd = crypt($pass, substr(crypt($id), -2));

パスワードの暗号化については、以下のフォームをご利用いただけます。ユーザー名とパスワードを入力して「パスワード生成」ボタンをクリックすると、.htpasswd ファイルに記述する形式でユーザー名とパスワードを出力します。コピーして .htpasswd ファイルにそのままペーストすることができます。(後述の password_hash() 関数 を使ったパスワードの暗号化もご利用できます)

以下は、ユーザー名(usr1 と usr2)とパスワード(pw1 と pw2)を上記フォームを使って出力して作成した .htpasswd ファイルの例です。

ユーザを削除するには、テキストエディタなどでそのユーザの行ごと削除します。

usr1:X.pQAhaJ00pZk
usr2:d./iS81B6DgPY

パスワードファイル(.htpasswd)を隠す

.htpasswd は暗号化されていても悪意のある人に見られると、解読される可能性があります。そのため、このファイルを隠すようにします。確実なのは、サーバの公開領域(public_html や htdocs) 以下に設置しないことです。または、.htaccess ファイルを使って隠すようにします。

以下の2通りの方法があります。以下の例では、.htaccess ファイルも隠す(アクセスさせない)ようにしています。

<Files ~ "^\.(htaccess|htpasswd)$">
  deny from all
</Files>

以下は、AddHandler ディレクティブで .htpasswd ファイルを CGI スクリプトとして認識させることで、ファイルの内容を参照することができないようにさせる方法です。

AddHandler cgi-script htaccess
AddHandler cgi-script htpasswd
XAMPP での Basic 認証

ローカル環境(XAMPP)で Basic 認証を設定する場合、パスワードファイルの生成方法が異なります。

以下の例ではパスワードファイルの保存先を「c:\xampp\pwd\.htpasswd」としています。XAMPP はデフォルトでは C ドライブ直下の xampp フォルダにインストールされているので、その中に pwd というフォルダ(フォルダ名は任意)を作成しておきます。

この例では、.htaccess ファイルには以下を記述しています。

AuthType Basic
AuthUserFile c:\xampp\pwd\.htpasswd
AuthGroupFile /dev/null
AuthName "Login Password Required"
Require valid-user

パスワードファイルの生成

ローカル環境(XAMPP)の場合は、Apache で用意されている「htpasswd.exe」を使ってパスワードの暗号化及びパスワードファイルを生成することができます。

  • 書式:htpasswd オプション パスワードファイル名 ユーザー名
  • 新規に作成する場合はオプションに「-c」を指定
  • 既存のパスワードファイルにユーザーを追加する場合にはオプションには何も指定しない

以下は XAMPP が C ドライブ直下にインストールされている場合に「user01」のパスワードを生成する例です。

C:\xampp\apache\bin に移動して、「htpasswd -c c:\xampp\pwd\.htpasswd user01」と入力すると、パスワードを聞かれるので2回入力すると、.htpasswd ファイルが生成されます。

C:\xampp\apache\bin>htpasswd -c c:\xampp\pwd\.htpasswd user01
New password: ******
Re-type new password: ******
Adding password for user user01  

2人目以降のパスワードを追加する場合は「-c」を付けません。「-c」を付けると新規作成されてしまい、それまでに登録されていたユーザー情報が消えてしまいます。以下はユーザーを追加する例です。

C:\xampp\apache\bin>htpasswd c:\xampp\pwd\.htpasswd user02
New password: ******
Re-type new password: ******
Adding password for user user02

追加のユーザーのパスワードの暗号化は以下のフォームを使って生成することができます。

以下は PHP の password_hash() 関数を使ってパスワードを暗号化しています。

リダイレクト

リダイレクトとは、ドメインやページの URL (ファイル名やディレクトリ名)が変更された場合に用いられる自動転送処理のことです。

.htaccess ファイルを使ってリダイレクト(自動転送)するには、Redirect ディレクティブを使う方法と RewriteRule ディレクティブを使う方法があります。

Redirect ディレクティブ

以下は、Redirect ディレクティブを使って自動転送する構文です。「Apache Redirect ディレクティブ

Redirect [status] URL-path URL
  • URL-path:転送元の URL を指定します。
    転送元の URL は、/ で始まるルートパス(トップディレクトリからのパス)で指定します。
  • URL:転送先の URL を指定します。
    転送先の URL は、http: などのスキームで始まる絶対 URL を指定します。
  • [status]:オプション(以下参照)を指定することで、HTTP ステータスコードを返すことができます。status が指定されていないリダイレクトは、デフォルトの "temp" (HTTP ステータス 302) になります。
status 引数
キーワード HTTP ステータス 説明
permanent 301 恒久的に移転する場合に指定します。
temp 302 一時的に移転する場合に指定します。(デフォルト)
seeother 303 他のもので置き換えられたことを意味します。
gone 410 永久に 削除されたことを意味します。このステータスが使用された場合、URL 引数は省略されなければなりません。

Redirect ディレクティブは、パスが完全に一致したものがマッチします。正規表現を使ったマッチングを行いたい場合は、RedirectMatch ディレクティブを使用します。

301リダイレクト

301リダイレクトとは、サイトやページを恒久的に移転先へ転送することで、ユーザーや検索エンジンが正しいページにたどり着くことができるようにする方法です。301リダイレクトが使われるケースは以下のような場合です。(「ページの URL の変更と 301 リダイレクトの使用」から引用)

  • サイトを新しいドメインに移動し、できるだけスムーズに移行を行いたい場合。
  • ユーザーが複数の異なる URL を介してサイトにアクセスする場合。
  • 2 つのウェブサイトを結合し、無効になった URL へのリンクが正しいページにリダイレクトされるようにしたい場合など。

301リダイレクトにより、検索エンジンにリンク評価が引き継がれ、サイトの評価をある程度保つことができるので SEO 効果があるとされています。

Redirect ディレクティブを使った転送の場合、status 引数に permanent を指定することで、301リダイレクトになります。

例えば、現在のウェブサイトへの全アクセスを、新しいサイト http://example.com/ へ自動転送させたい場合は以下のように記述します。この場合、転送元(URL-path)が「/」で転送先(URL)が「http://example.com/」です。

http://xxxxxx.net/foo.html にアクセスした場合、http://example.com/foo.html にリダイレクトされます。転送先に対応するファイルがない場合はエラー(Error 404)になります。

Redirect permanent / http://example.com/

特定のディレクトリ /foo/ を、別のディレクトリ http://www.example.com/bar/ へ転送させたい場合は、以下のように記述します。この場合も、転送先に対応するファイルがない場合はエラー(Error 404)になります。

Redirect permanent /foo/ http://www.example.com/bar/

特定のページ /foo/index.html を、別のページ http://www.example.com/bar/index.php へ転送させたい場合は、以下のように記述します。

Redirect permanent /foo/index.html http://www.example.com/bar/index.php

RedirectMatchディレクティブ

RedirectMatch ディレクティブは、Redirect ディレクティブと機能はほぼ同じですが、正規表現を用いたマッチングを行う点が異なります。以下が書式です。

RedirectMatch [status] regex URL

例えば、すべての GIF ファイルを別サーバの同様な名前の JPEG ファイルに後方参照を利用してリダイレクトするには、以下のように記述します。

RedirectMatch (.*)\.gif$ http://www.anotherserver.com$1.jpg 

Apache RedirectMatch ディレクティブ

.htaccess が使えない場合

使っている環境で .htaccess によるリダイレクトが使用できない場合には、以下のような方法もあります。

meta refresh

meta 要素の http-equiv 属性に "refresh" を設定することにより、リダイレクト(転送)を指定することができます。content 属性で指定した秒数後に、指定した URL へリダイレクトされます。

以下が書式です。これを <head> 内に記述します。

<meta http-equiv="refresh" content="秒数; URL=http://移転先ドメイン/"> 

以下の例では meta 要素の content 属性で指定する秒数を0秒に指定してあるのでそのページにアクセスするとすぐに指定された URL にリダイレクトされます。

<meta http-equiv="refresh" content="0; URL=http://example.com/">

秒数を「0秒」で指定している場合、検索エンジン側の扱いは301リダイレクトになるという話もありますが、定かではありません。

または、0秒ではなく5秒後くらいの待機時間を設定して、元のページには移転先のページへ訪れるような説明と移転先へのリンクも用意しておくという方法もあります。

PHP header() 関数

PHP を使って別のページへリダイレクトさせるには、header() 関数で Location ヘッダーを使用します。

注意点としては、header() 関数以下のスクリプトが実行されないように、必ず exit() 関数を使ってスクリプトを終了するようにします。

以下はいずれも PHP を使った301リダイレクトの例です。詳細は「PHP リダイレクト」を参照ください。

<?php
header('HTTP/1.1 301 Moved Permanently');
header('Location: http://example.com/');
exit();
?>
<?php
header('Location: http://example.com/', true, 301);
exit();
?>

mod_rewrite を使ったリダイレクト

サーバーの拡張機能 mod_rewrite の機能が使えれば、RewriteRule ディレクティブを使ったリダイレクトを設定することができます。

mod_rewrite を使ったリダイレクトの基本的な形は以下のようなものになります。

RewriteEngine On
RewriteBase /
RewriteRule 転送元ファイル正規表現 転送先URL [フラグ] 
  • RewriteEngine:
    mod_rewrite の機能の On/Off を指定します。デフォルトの値は Off になっているので、URL の書き換え処理を行うには、On にする必要があります。
  • RewriteBase:

    RewriteRule ディレクティブで指定するリダイレクト先の URL 書き換えの基準となるディレクトリを相対パスで指定します。RewriteRule の転送先を相対パスで記述した場合にのみ適用されます。絶対パスで指定している場合は不要です。

    省略した場合には実際の URL の階層(.htaccess が置かれたディレクトリからの相対パス)が適用されます。

    例えば、/var/www/html/foo/ に設定した場合、デフォルト(省略した場合)の RewriteBase は /foo/ ということになります。これは、/foo/test.html にアクセスがあったら test.html に対してマッチングが行われ、リダイレクト先として sample.html が指定されていたら/foo/sample.htmlにリダイレクトされるということになります。

    「RewriteBase /」と記述した場合、どのディレクトリに設置を行った場合でも必ずドキュメントルートからのパスになります。

    転送元の基準ではなく、転送先の URL の基準であることに注意が必要です。(転送先が絶対 URL ・絶対パスで指定していれば関係ありません)

  • RewriteRule:
    具体的な URL 書き換えのルールを記述します。1番目のパラメータである正規表現にマッチした場合、2番目のパラメータである URL に書き換えられます。(RewriteRule ディレクティブ
  • フラグ:
    省略可能ですが、301リダイレクトにするには R=301 を指定する必要があります。また、合わせてよく指定される「L」は URL の書き換え処理を終了(last)を意味します。

以下は、特定のページから特定のページにリダイレクトさせる場合の例です。

foo.html にアクセスがあった場合に、http://www.example.com/bar.html へ301リダイレクトさせる設定です。同一ドメイン内で URL が変更になった場合でも、別のドメインの URL でも指定が可能です。

RewriteEngine On
RewriteRule ^foo.html$ http://www.example.com/bar.html [R=301,L] 

以下は、ディレクトリ単位でリダイレクトさせる場合で、転送先にファイル名を引き継ぎ、同じファイル名($1)として別ディレクトリに転送したい場合の例です。

foo というディレクトリにアクセスがあった場合、http://www.example.com/bar/ というディレクトリに同じファイル名($1)としてリダイレクトする設定です。

RewriteEngine On
RewriteRule ^foo/(.*)$ http://www.example.com/bar/$1 [R=301,L]

以下は、ディレクトリ単位でリダイレクトさせる場合で、転送先にファイル名を引き継がず、同一の URL に転送したい場合の例です。

foo というディレクトリにアクセスがあった場合、http://www.example.com にリダイレクトする設定です。

RewriteEngine On
RewriteRule ^foo/(.*)$ http://www.example.com [R=301,L]

IfModule ディレクティブ

以下のように、IfModule ディレクティブを記述しているのを見かけます。

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule  ......
</IfModule>   

上記の例の場合、使用している環境で mod_rewrite が利用できるかどうかを確認しています。利用できない場合は、<IfModule mod_rewrite.c> ~ </IfModule>の中のコードが適用されません。

以下が IfModule ディレクティブの書式です。

<IfModule [!]module-file|module-identifier> ... </IfModule> 

IfModule ディレクティブは、モジュールが存在する(または存在しない)場合に処理されるディレクティブを指定することができます。モジュールが存在しない条件の場合、! をモジュールのファイル名の前に付けると、そのモジュールが存在しない場合に処理されます。

module 引数(module-file|module-identifier)は、モジュール識別子か コンパイルをした時のモジュールのファイル名です。 例えば、rewrite_module は識別子で mod_rewrite.c はファイル名です。

mod_rewrite が利用できない環境で mod_ rewrite のコードを記述するとエラーが発生してしまうので、利用できるか定かでない場合は、IfModule ディレクティブを使って mod_rewrite が使えるのかを確認することができます。

利用できることがわかっている場合は、不要です。Apache のマニュアルには「特定のモジュールの存在に関わらず動作する設定ファイルの原本が必要なときにのみこのセクションを使用してください。 通常の動作では、ディレクティブを <IfModule> セクションの中に入れる必要はありません。」とあります(<IfModule> ディレクティブ)。

RewriteCond ディレクティブ

RewriteCond ディレクティブは、RewriteRule(書き換えルール)を実行するための条件を定義します。RewriteCond に記述した条件を満たした場合のみ、直後の RewriteRule ディレクティブの書き換えが行われます。条件を満たさない場合は、直後の RewriteRule ディレクティブは実行されません。

RewriteCond ディレクティブは、RewriteRule ディレクティブの前に 1 つ以上設置することができ、複数設置する場合は、オプション(フラグ)によって OR 条件として処理するかを指定することができます。オプションを省略した場合は、AND 条件として処理されます。

以下が基本的な書式です。

RewriteEngine On
RewriteCond  テスト文字列  条件パターン  [フラグ]
RewriteRule  条件  置換文字列 

オプション(フラグ)

RewriteCond で使用できるオプション(フラグ)は以下になります。

RewriteCond で使用できるオプション
オプション 意味
[OR] 連続する RewriteCond のいずれかが true の場合に実行します。省略時は、AND 条件として処理され、連続する RewriteCond がすべて true の場合に実行します。
[NC] 条件を評価する時、大文字小文字を区別しません。これを指定しないと大文字小文字を区別します。

OR と NC をどちらも指定したい場合には [OR,NC] とカンマ区切りで指定します。

条件を複数指定する場合 (AND 条件): 複数の条件が全て一致した場合に RewriteRule を実行

RewriteCond  テスト文字列  条件パターン
RewriteCond  テスト文字列  条件パターン            
RewriteRule  条件  置換文字列

条件を複数指定する場合 (OR 条件):複数の条件のいずれかが一致した場合に RewriteRule を実行

RewriteCond  テスト文字列  条件パターン [OR]
RewriteCond  テスト文字列  条件パターン            
RewriteRule  条件  置換文字列 

後方参照

RewriteCond の条件パターン(正規表現)で括弧を使って一致した値は、変数を使って再利用できます。RewriteCond で指定したパターンを参照する場合は「%n」を使います(n は1~9の数字)。括弧が複数ある場合は左から %1、%2 というように指定します。

RewriteRule ディレクティブでも後方参照を利用できます。RewriteRule ディレクティブの場合は、通常の正規表現の後方参照と同じように「$n」を使用します(n は0~9の数字)。

テスト文字列に利用できる環境変数

具体的な例を見てみます。以下は、「www なし」でアクセスがあったら、「www あり」に 301 リダイレクトさせる例です。テスト文字列に %{HTTP_HOST} とありますが、これは「サーバーのホスト名」を表します。mod_rewrite では「HTTP 環境変数」という特別な変数を利用でき、%{環境変数名} と指定することで利用することができます。

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example¥.com$  [NC]
RewriteRule ^(.*)$ http://www.example.com/$1 [R=301,L]

以下は、利用できる「HTTP 環境変数」の一例です。

HTTP 環境変数の一例
環境変数 値・意味
HTTP_HOST リクエスト先のホスト名 (DNS 名) 情報。http://www.example.com/~ でアクセスがあった場合、%{HTTP_HOST} は www.example.com
HTTP_REFERER 参照元の URL 情報(前にいたページのURL情報)
HTTP_ACCEPT ブラウザがサポートするメディア・MIMEタイプ情報
HTTP_USER_AGENT ユーザのブラウザや端末情報
HTTPS HTTPS であれば on、そうでなければ off を返す
QUERY_STRING クエリ文字列
SERVER_ADDR サーバーのアドレス
SERVER_PORT サーバーのポート番号
PATH_INFO ファイル名とクエリ文字列の間にあるパス情報
REMOTE_ADDR ユーザの IP アドレス情報
REMOTE_USER リモートユーザー名 (Basic 認証利用時)
REMOTE_HOST リモートホスト名(ユーザーのホスト名)
THE_REQUEST リクエスト文字列(ユーザーのブラウザから送られてきた HTTP リクエスト行:例 GET /index.html HTTP/1.1)
REQUEST_URI リクエストされた URI 情報。http://example.com/foo/bar.html にアクセスされた場合は /foo/bar.html になります。また、REQEST_URI はクエリ文字列を含みます。
REQUEST_FILENAME リクエストされたファイル名(ファイルシステム内の絶対パス)。REQUEST_FILENAME が存在しない場合は、SCRIPT_FILENAME に同じ値が入っているのでそれを利用可能。
SCRIPT_FILENAME 実行するスクリプトの絶対パス情報。ルート直下の phpinfo.php の場合、/home/username/public_html/phpinfo.php。環境により異なる。XAMPP の場合、 C:/xampp/htdocs/xampp/phpinfo.php
TIME_YEAR
TIME_MON
TIME_DAY
TIME_WDAY 曜日 (0:日 ~ 6:土)

条件パターンで使える演算子

RewriteCond では条件に一致するパターンを Perl 互換の正規表現で指定します。以下は条件パターンの例です。

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

1行目は、%{REQUEST_FILENAME} (リクエストされたファイル名)が「!-f」という条件を満たしていれば true となります。「!-f」は「!」と「-f」に分割することができます。

「-f」はファイルが存在する場合(ファイルである場合)は true を返します。「!」は否定(結果を逆転させるもの)です。よって、「!-f」はファイル以外の(ファイルが存在しない)場合は、true を、ファイルの場合は false を返します。

2行目は、「-d」がディレクトリかどうかの検証を行い、%{REQUEST_FILENAME} がディレクトリの場合(そのディレクトリが存在する場合)は true を、それ以外の場合は false を返しますが、1行目と同様に「!」がついているので、ディレクトリ以外の(ディレクトリが存在しない)場合に、true を、ディレクトリの場合は false を返します。

これら2つの条件にはオプション[OR]を指定していないので、AND 条件として処理され、「ファイルが存在せず」かつ「ディレクトリが存在しない」という条件になります。

以下は利用できる演算子の一例です。

利用できる演算子の一例
演算子 意味
! 否定
< テスト文字列より大きい場合は true
> テスト文字列より小さい場合は true
= テスト文字列と等しい場合は true
<= テスト文字列以上の場合は true
>= テスト文字列以下の場合は true
-d ディレクトリが存在する場合は true
-f ファイルが存在する場合は true
-s ファイルが存在し、サイズが0でない場合は true

RewriteRule ディレクティブ

RewriteRule ディレクティブは、URL の書き換えを行うディレクティブです。「条件パターン」に一致する URL を、その後の「置換文字列」で書き換えます。

条件パターンは(Perl 互換の)正規表現で指定することができます。以下が書式です。

RewriteRule 条件パターン 置換文字列 [フラグ]

ユーザーからリクエストのあった URL(サーバーから渡された URL)が、条件パターンにマッチした場合、置換文字列で書き換えられます。

  • 条件パターン:URL とマッチさせる部分です。正規表現を利用することができます。
  • 置換文字列:URLと条件パターンがマッチした場合に、行う処理を指定する部分です。
  • [フラグ]:挙動を制御するためのオプションを指定できます。複数のフラグを指定する場合は、カンマ区切りで指定します。

以下は「/index.html」でアクセスがあった場合、「/」に 301 リダイレクトさせる例です。

RewriteEngine On
RewriteRule ^index\.html$ http://example.com/ [R=301,L]

上記の場合、例えば、http://example.com/index.html にアクセスがあった場合、RewriteRule には index.html が渡されます(/index.html ではありません)。そしてパターン ^index\.html$ とマッチするか検証されます(^ は行頭、$ は行末、\ はエスケープを表す正規表現になります)。この場合はマッチするので、http://example.com/ に置換(リダイレクト)されます。

[R=301,L] は R=301 と L が指定されています。

  • R=301:R はリダイレクトの R で、「R=301」とすると、301リダイレクトを指定する事になります。
  • L:Last の L で、 L が指定されている RewriteRule の条件にマッチした場合、以降の RewriteRule を処理しない事を意味します。但し、 .htaccess では L フラグが有効になりません。.htaccess では再探索が優先されます。

以下は、RewriteRule で利用可能なフラグの例です。

フラグ (別名) 意味
R[=code] redirect 指定した URL にリダイレクトします。[R=301] のようにレスポンスコードを指定することができます。指定しない場合は 302(Moved Temporarily) でリダイレクトされます。temp(デフォルト値:302)、permanent(301)、seeother(303) のシンボル名も指定できます。
F forbidden ステータスコード「403」(Forbidden)を返します。RewtiteCond に設定した条件で、Webサーバへのアクセスを禁止する場合などに指定します。
G gone ステータスコード「410」(Gone)を返します。ユーザーに、存在しないページにアクセスしていることを通知する場合に指定します。
P proxy ルールにマッチしている場合は、書き換えたURLをプロキシ要求とみなし、リバースプロキシとして動作します。このフラグを使用する場合は、mod_proxy が組み込まれている必要があります。
L last ルールにマッチしている場合は、URLの書き換え処理を終了します。後続のルールは適用されません。但し、 .htaccess では「L」フラグが有効になりません。
N next 一連の書き換え処理のルールを先頭のルールから再度実行します。すでに書き換えられたURLが対象となります。本フラグを指定する場合は、書き換え処理が無限に実行されないように、適切に終了条件を設定する必要があります。
C chain ルールにマッチしている場合は、後続のルールを適用します。ルールにマッチしていない場合、後続のルールはすべて適用されません。
T=MIME-type type=MIME-type ルールにマッチしている場合は、指定した MIME タイプを Content-Type ヘッダに設定します。
NS nosubreq Webサーバ内で発生するサブリクエストには、ルールを適用しません。ユーザーから末尾がスラッシュ(/)のURLが指定された際に、DirectoryIndex ディレクティブで指定したファイルに対するサブリクエストが発生しますが、そのサブリクエストはルールを適用しない場合などに使用します。
NC nocase 大文字小文字を区別せずにルールを適用します。
QSA qsappend 書き換え前のURLおよび置換文字列にそれぞれクエリ文字列(URL内のクエスチョンマーク(?)以降の文字列)が存在する場合、置換文字列の末尾にアンパサンド(&)と書き換え前の URL に指定されたクエリ文字列を追加します。本フラグを指定しない場合、クエリ文字列は置換文字列で上書きされます。
NE noescape 書き換え時のURLエスケープを抑制します。通常、パーセント(%)やセミコロン(;)などの特殊文字は、16進表現の“%25”、“%3b”にエスケープされますが、本フラグを指定した場合は、エスケープされません。
PT passthrough URLの書き換え後、他のモジュールに制御を渡します。書き換えたURLに対して、Aliasディレクティブ、ScriptAliasディレクティブ、およびRedirectディレクティブなどを使用する場合に指定します。
S=num skip=num ルールにマッチしている場合、数値に指定した数の後続のルールの適用をスキップします。
E=環境変数:値 env=環境変数:値 ルールにマッチしている場合、指定した任意の環境変数に値を設定します。値には、置換文字列と同様に、$n および %n も指定できます。

RewriteRule を複数記述する場合

RewriteRule を複数記述した場合、処理は先頭行から条件に一致する行を探索していきます。条件に一致した場合は、先頭行に戻り、再度条件に一致する行を探索します(再探索)。

以下の例の場合、aaa.html にアクセスすると bbb.html に書き換えられて、再度探索が行われ最終的に ccc.html に書き換えられます。

RewriteEngine On
RewriteRule aaa.html bbb.html
RewriteRule bbb.html ccc.html

.htaccess ファイルで URL の書き換えを行うと、書き換えが終わった後にもう一度書き換え設定を上から順に適用し、書き換えの必要がないかをチェックし、書き換える必要が無くなるかループでエラーになるまでこれが続きます。

以下の場合、aaa.html にアクセスすると bbb.html に書き換えられて、再度探索が行われ aaa.html に書き換えられ、... 無限ループを引き起こし、500 internal server error が発生してしまいページ閲覧ができなくなるので注意が必要です。

RewriteEngine On
RewriteRule aaa.html bbb.html
RewriteRule bbb.html aaa.html

[L] フラグを設定すれば良さそうですが、.htaccess では [L] フラグが有効にならず(httpd.conf では [L] フラグが正常に動作します) .htaccess では再探索が優先されます。無限ループを防ぐには RewriteCond を使用するのが一般的です。

以下の場合、http://xxxx.com/index.html にアクセスした場合、index3.html 書き換えられそうですが、最終的には index4.html に書き換えられます。

RewriteEngine On
RewriteRule index.html index2.html
RewriteRule index3.html index4.html
RewriteRule index2.html index3.html
  • http://xxxx.com/index.html にアクセスした場合、RewriteRule に index.html が渡されます。
  • 2行目:index.html はパターンマッチして index2.html に書き換えられます。
  • 3行目:index2.html はパターンマッチしません。
  • 4行目:index2.html はパターンマッチして index3.html に書き換えられます。
  • 最初に戻ります(再探索)。
  • 2行目:index3.html はパターンマッチしません。
  • 3行目:index3.html はパターンマッチして index4.html に書き換えられます。
  • 再探索しますが、これ以降はパターンマッチしないので終了になります。

www あり・なしの統一

www ありの URL を、www なしの URL にリダイレクト、または、その逆の処理を行います。

www なしに統一

www.exmaple.com でアクセスがあった場合に exmaple.com に正規化(リダイレクト)する方法です。ドメイン部分を自分の環境に書き換えて、以下を記述した .htaccess をルートディレクトリに設置します。

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

http://www.example.com/~ でアクセスがあった場合、%{HTTP_HOST} は www.example.com になります。RewriteCond 条件パターン部分の ^www\.example\.com とマッチするので、処理は RewriteRule に移行してリダイレクトされます。 [NC] は大文字小文字を区別しないフラグです。

$1 はリクエストされたページの URL を後方参照で、正規化済みの URL の末尾に加えています。また、R=301 を指定して 301リダイレクトにしています。L は書き換えの終了(Last)を意味します。

以下は、ポート番号(:80)のある場合も考慮した記述例です。後方参照の変わりに %{REQUEST_URI} を使っています。また、^(.*)$ と (.*) はほぼ同じ意味です。

RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.example\.com)(:80)? [NC]
RewriteRule (.*) http://example.com%{REQUEST_URI} [R=301,L]

%{REQEST_URI} はブラウザから送られてきたリクエスト URI を意味します。例えば http://example.com/foo/bar.html にアクセスされた場合は /foo/bar.html になります。また、REQEST_URI はクエリ文字列を含みます。

以下はドメイン名をハードコーディングしない(記述しない)方法です。www\. の \ はエスケープです。(.+)は任意の文字の1回以上の繰り返しで、括弧で括って後方参照します。ホスト名が www. で始まる場合というような意味になります。次の行の RewriteRule で (.+) を %1 で利用しています。3行目の ^(キャレット)は、文字列の先頭を意味するので、その後の文字を調べずに全ての文字列(空白にも)にマッチします。^$(改行やタブも含まない空行)にもマッチします。(RewriteRule - Caret ^ - Match

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]

www ありに統一

前述とは逆に exmaple.com でアクセスがあった場合に www.exmaple.com に正規化(リダイレクト)する方法です。ドメイン部分を自分の環境に書き換えて、以下を記述した .htaccess をルートディレクトリに設置します。

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

以下でも同じ結果が得られます。

RewriteEngine On
RewriteCond %{HTTP_HOST} ^(example\.com)(:80)? [NC]
RewriteRule (.*) http://www.example.com%{REQUEST_URI} [R=301,L]

以下はドメイン名をハードコーディングしない方法です。2行目は %{HTTP_HOST}(ホスト名)が www. から始まらない場合は(! は否定)、という意味になります。この条件にマッチした場合は、次の行の RewriteRule が適用され、ホスト名に www が付けられます。ファイル名は $1 で参照しています。

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

index.html なしに統一

/index.html でアクセスがあったら、/ に301リダイレクトさせる方法です。例えば、http://www.example.com/index.html にアクセスがあった場合、http://www.example.com/ にリダイレクト(正規化)するには、以下のように記述します。

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

%{THE_REQUEST} には、 HTTP リクエスト行(例 GET /index.html HTTP/1.1)が入っています。条件パターン ^.*/index\.html にマッチすれば、次の行の RewriteRule が適用され、/index.html が削除されます。

index.html だけではなく、index.php も対象とするには、以下のように記述します。

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

https スキームに統一

http からはじまる URL へのアクセスを https へリダイレクトさせる例です。

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

%{HTTPS} は HTTPS であれば on、そうでなければ off を返します。off の場合、https で始まる URL にリダイレクトさせます。3行目の括弧は後方参照を使っていないので、省略可能です。

%{HTTP_HOST} はホスト名、%{REQUEST_URI} はクエリ文字列を含む URI 情報です。http://example.com/foo/bar.html にアクセスされた場合、%{HTTP_HOST} は example.com、%{REQUEST_URI} は/foo/bar.html になります。

http スキームに統一

前述の逆で、http スキームに統一するには、以下のように記述します。

RewriteEngine On
RewriteCond %{HTTPS} on
RewriteRule ^(.*)$ http://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]        

拡張子なしでのアクセス

通常 URL にはファイルの拡張子が含まれますが、拡張子なしでもアクセスできるようにする方法です。

http://exmaple.com/sample.html でも、
http://exmaple.com/sample でも同じファイルにアクセスできるようにする例です。

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html 

%{REQUEST_FILENAME} !-d は、リクエストされたファイル名のディレクトリが存在しない場合は、という条件です。

%{REQUEST_FILENAME}\.html -f は、リクエストされたファイル名に .html が付いたファイルが存在する場合は、という条件です。

これら2つの条件にはオプション[OR]を指定していないので、AND 条件として処理されて、書き換え処理が行われ、結果として拡張子なしでアクセスしても拡張子の付いたファイルが表示されます。

.php など他の拡張子の場合は、上記の html の部分をその拡張子に書き換えれば利用できます。

以下は、html と php が混在している場合に対応する例です。

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html 

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php

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

どの URL にアクセスしても「現在メインテナンス中です」というメインテナンスページを表示する方法です。まず、メインテナンスページ(例:maintenance.html 任意の名前)を用意して、.htaccess に以下のように記述します。以下の例では、メインテナンスページはドキュメントルートに配置しています。(/maintenance.html)

ErrorDocument 503 /maintenance.html
     
RewriteEngine On
RewriteCond %{REQUEST_URI} !=/maintenance.html
RewriteCond %{REMOTE_ADDR} !=xxx.xxx.xxx.xxx
RewriteCond %{REQUEST_FILENAME} !\.(css|jpe?g|gif|png|js)$
RewriteRule ^.*$ - [R=503,L]

Header set Retry-After "Sun, 06 Aug 2017 6:00:00 GMT"
  • 1行目:ErrorDocument ディレクティブで、HTTP ステータスコード 503 (Service Unavailable:サービス利用不可)を返すようにして、メインテナンスページ(maintenance.html)へリダイレクトします。*単にメインテナンスページにリダイレクトするだけだと、HTTP ステータスコードが 200 となるため、コンテンツとしてキャッシュされてしまいます。また、HTTP 404 (Not Found:未検出) を設定すると、クローラーにサイトが無くなったものと判断される可能性があります。
  • 4行目:RewriteCond で、メインテナンスページを除外しています。
  • 5行目:作業者の IP アドレスを除外しています(メインテナンスページが表示されてしまうと作業ができないため)。複数ある場合は、行を増やして追加します。
  • 6行目:CSS や JavaScritp、画像ファイルなどのリソースファイルを除外しています。(メインテナンスページから参照できるようにするため)
  • 7行目:除外した以外の全てのページにステータスコード 503 を設定します。ハイフン(-)はフラグの操作だけを実行したい(置換しない)場合に使います。
  • 9行目:メンテナンス終了予定時刻(GMT で指定)を設定します。

メンテナンスを知らせるページを作っている時間がない(緊急にメンテナンスをおこなければならない)場合は、以下を .htaccess に追記すれば、ユーザーには「Sorry. We’re under maintenance.」というメッセージを表示し、検索エンジンには503を返すことができます。メッセージは任意のものを設定できますが、半角英数字が無難です。

ErrorDocument 503 "Sorry. We're under maintenance."
RedirectMatch 503 .*

エラーページの変更

ErrorDocument ディレクティブを使って、エラードキュメントの表示方法の設定をすることができます。(Apache コア機能 ErrorDocument ディレクティブ

以下が書式です。error-code にはステータスコードを、document には、表示するテキストまたはファイルへのパスを指定します。

ErrorDocument error-code document

ErrorDocument ディレクティブを使って、問題やエラーが発生したときに、 以下の四つのうちのいずれかの動作を設定することができます。

  1. Apache 標準の簡単なエラーメッセージを表示(デフォルトなので特に設定は不要)
  2. 自分で指定したメッセージを表示
  3. 問題やエラーの処理をする為に、自サーバ内の URL-path へリダイレクト
  4. 問題やエラーの処理をする為に、外部の URL へリダイレクト

自分で指定したメッセージを表示

自分で指定したメッセージを表示を表示する場合は、エラーコードの後に、メッセージをダブルクォート(")で囲んで記述します。

ErrorDocument 404 "Sorry, the file not found"

問題やエラーの処理をする為に、自サーバ内の URL-path へリダイレクト

自分のサーバーに表示するファイルを配置する場合は、/ で始まるドキュメントルートからの相対パスを指定します。以下は 404.html というファイルをルートディレクトリに配置した場合の例です。

401(Unauthorized:認証失敗)や 404(Not Found:未検出)の際にファイルを指定する場合はこの方法を使います。

ErrorDocument 404 /404.html

問題やエラーの処理をする為に、外部の URL へリダイレクト

http などで始まる完全な URL を指定します。この場合、クライアントはエラーステータスコードを受け取らずに、リダイレクトのステータスコードを受け取るので注意が必要です。(※)

ErrorDocument 500 http://foo.example.com/cgi-bin/tester

※重要:
リモート(外部) URL を ErrorDocument に指定すると、 たとえ文書が同じサーバにあっても、ドキュメントがどこにあるかを通知するために、 Apache はリダイレクトをクライアントに送出します(クライアントはエラーステータスコードを受け取らずに、リダイレクトのステータスコードを受け取ります)。このため、ステータスコードを使って URL が有効であるかどうかを決定しようとする ウェブロボットやその他クライアントを、混乱させる可能性があるので注意が必要です。

また、ErrorDocument 401 にリモートの URL を指定すると、 クライアントは 401 というステータスコードを受け取らないため、 パスワードをユーザーに入力要求しなければならないことがわかりません。 そのため、ErrorDocument 401 というディレクティブを使う場合は、 内部サーバーの文書を参照する必要があります。

同様に ErrorDocument 404 の場合も、内部サーバーの文書を参照するようにして、必ずウェブサーバーからユーザーやクローラーに 404 ステータスコードを返して、検索エンジンがカスタム 404 ページを誤ってインデックスに登録しないようにする必要があります。

また、Internet Explorer では、サーバが生成したエラーメッセージが 512 Byte より小さい場合、Internet Explorer 独自ののエラーメッセージを表示します。エラーメッセージが 512 Byte より大きい場合は、サーバが生成したエラーメッセージを表示します。以下は IE 独自ののエラーメッセージの例です。

Internet Explorer 独自ののエラーメッセージの例

代表的なステータスコード(エラーコード)
Status-Code エラー内容 意味
401 Unauthorized ユーザー認証が必要なページで認証に失敗した(ID、パスワードのタイプミスなど)
403 Forbidden 指定されたリソースへのアクセス権がない(許可されていないディレクトリにアクセスしようとしたなど)
404 Not Found 指定されたリソース(ファイル)が存在しない(URLタイプミスなど)
500 Internal Server Error サーバがリクエスト実行できない(CGIのバグや .htaccess などのサーバ設定ファイルに記述ミスがある場合など)
501 Not Implemented リクエストを処理できる機能がサーバにない
503 Service Unavailable サービス利用不可

404 ページの作成

以下は Google ウェブマスターツールの「有益な 404 ページを作成する」からの抜粋です。

404 ページとは、ユーザーがサイト上で存在しないページにアクセスしようとした(切れたリンクをクリックした、ページが削除されている、URL のつづりを間違えて入力したなど)ときに表示されるページのことです。

存在しないページのリクエストに応答する際、ページが存在しないことを示す HTTP ステータス コード 404 をウェブサーバーが返すため、このページは 404 ページと呼ばれています。

標準の 404 ページは ISP によって異なることがありますが、通常はユーザーに有用な情報が提供されることはないため、ほとんどのユーザーはそのまま別のサイトに移動してしまいます。

サーバーへのアクセス権がある場合は、独自の 404 ページを作成することをおすすめします。404 ページをわかりやすくカスタマイズすることにより、探している情報の場所をユーザーに知らせたり、役に立つ他のコンテンツを提供したり、サイト内をさらに探すよう促したりできます。

以下は 404 Not Found エラーメッセージを、独自のエラーページ(カスタム 404 )に置き換える大まかな手順です。

  • エラーの際に表示する 404 ページを作成し同じサーバー内に保存します(ファイル名は任意)。この例では 「404.html」
  • 作成した「404.html」をアップロードします。
  • .htaccess ファイルを編集します。

.htaccess の記述

.htaccess に以下のように 404 ページのファイル(404.html)へのパスを / で始まるドキュメントルートからの相対パスで指定して記述します。以下はルートディレクトリに配置した場合の例です。

ErrorDocument 404 /404.html

「error」ディレクトリに 404.html を配置した場合

ErrorDocument 404 /error/404.html

404 ページを作成する場合に考慮する点

有益な 404 ページを作成する」には、以下のようなことも書かれています。

  • ユーザーに対して、探しているページが見つからないことを明確に伝える。
  • 404 ページを、サイトのその他の部分と同じデザイン(ナビゲーションを含む)にする。
  • 最も人気のある記事や投稿へのリンクの他、ホームページへのリンクを追加する。
  • 404 ページが Google や他の検索エンジンのインデックスに登録されないようにするため、存在しないページがリクエストされたときにウェブサーバーが実際の 404 HTTP ステータス コードを返すことを確認する。

以下は FireFox の Firebug を使ってステータスコードを確認する例です。

ブラウザでのステータスコードの確認の例

また、可能であれば以下のようなことも考慮すると良いと思います。

  • サイト内検索のフォームを設置する
  • サイトマップを表示する

ファイル内のパスは「ルートパス」または「絶対パス」を使用

「404 ページ」は様々な場所から参照・呼び出しされるので画像やファイル、リンクへのパスは「/」から始まるルートパス(トップディレクトリからのパス)または絶対パス(URL)で指定する必要があります。

以下のような画像や CSS ファイル等は「ルートパス」または「絶対パス(URL)」で指定します。

  • 画像へのパス
  • 表示するリンクのパス(ナビゲーションやトップページへのリンク等)
  • CSS や JavaScript のパス
  • 場合によっては CSS や JavaScript は head 内などに記述することも考えられます

以下は最低限の内容を記述した 404 ページの例です。実際にはそのサイトと同じようなデザイン(ナビゲーションを含む)にすることが望ましいです。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>404 Not Found</title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body>
<h1>404 Not Found...</h1>
<p><img src="/images/error_message.jpg" alt="エラーイメージ" ></p>
<p>指定された以下のページは存在しないか、または移動した可能性があります。</p>
<p class="error_url"></p>
<p><a href="http://www.example.com/">トップページへ </a></p>
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script> 
<script>
jQuery(function($){ 
    $('p.error_url').text(location.href);
});
</script>
</body>
</html>

上記で記述している jQuery は JavaScript の「location.href」を使って指定された URL を取得して表示するためのものです。

キャッシュの制御

Web ページの表示速度を改善する方法の一つに、ブラウザキャッシュの利用があります。キャッシュをうまく制御することによって、応答時間の短縮やサーバリソースの節約が可能になります。

以下は「PageSpeed Insights:ブラウザのキャッシュを活用する」からの引用です。

Expires ヘッダーと Cache-Control: max-age ヘッダー

これらのヘッダーでは、ウェブ サーバーから新しいバージョンが提供されているかどうか確認せずに、ブラウザでキャッシュ済みのリソースを使用できる期間を指定します。無条件で適用される「強いキャッシュ ヘッダー」です。設定後にリソースがダウンロードされると、有効期限や最大期間に達するかユーザーがキャッシュを消去するまで、ブラウザはそのリソースに対する GET リクエストを発行しません。

Last-Modifed ヘッダーと ETag ヘッダー

これらのヘッダーでは、キャッシュする目的で、ファイルが同じかどうかをブラウザが判断する方法を指定します。Last-Modified ヘッダーでは、日付を使用します。ETag ヘッダーでは、リソースを一意に識別する値を使用します(ファイルのバージョンやコンテンツ ハッシュが一般的です)。Last-Modified は、ブラウザがキャッシュからアイテムを取得するかどうかを判断する際にヒューリスティック(経験則)を適用するという意味で、「弱い」キャッシュ ヘッダーです。

どちらのキャッシュ ヘッダーを使用すべきか

キャッシュできるすべてのリソースで、Expires と Cache-Control: max-age のいずれか、Last-Modified と ETag のいずれかを指定することが大切です。Expires と Cache-Control: max-age の両方、または Last-Modified と ETag の両方を指定すると冗長になります。

また、「推奨される解決方法」としては、以下のように書かれています。

サーバーでブラウザのキャッシュを有効にします。静的なリソースのキャッシュの有効期間は 1 週間以上にしてください。広告やウィジェットなどのサードパーティ リソースの場合は、キャッシュの有効期間を 1 日以上にします。キャッシュ可能なすべてのリソースで、次のような設定をおすすめします:

  • Expires を 1 週間以上、できれば最大で 1 年間先に設定します(より広くサポートされているため、Cache-Control: max-age よりも Expires をおすすめします)。RFC のガイドラインに違反するので、1 年以上先には設定しないでください。
  • リソースの変更時期が正確にわかっている場合は、短い有効期間を設定してもかまいません。ただし、すぐに変更される可能性があるものの、変更時期がわからない場合は、長い有効期間を設定して、URL フィンガープリントを使用してください。

キャッシュ関連参考サイト:

mod_expires

mod_expires は、Expires ヘッダと Cache-Control ヘッダの max-age の設定を制御します。元のファイルが作成された時刻または クライアントのアクセス時刻のどちらかに基づいて期限切れ日を設定することができます。(Apache モジュール mod_expires

Expires ヘッダの設定

以下は Expires ヘッダの設定の記述例です。<ifModule mod_expires.c>(ifModule ディレクティブ)は mod_expires が有効で利用できるとわかっている場合は不要です。

<ifModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 10 minutes"
  ExpiresByType text/html "access plus 10 seconds"
  ExpiresByType image/jpg "access plus 7 days"
  ExpiresByType image/jpeg "access plus 7 days"
  ExpiresByType image/gif "access plus 7 days"
  ExpiresByType image/png "access plus 7 days"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/pdf "access plus 1 month"
  ExpiresByType text/x-javascript "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
  ExpiresByType application/x-javascript "access plus 1 month"
  ExpiresByType application/x-shockwave-flash "access plus 1 month"
  ExpiresByType image/x-icon "access plus 1 month"
</ifModule>

ExpiresActive ディレクティブ

2行目の ExpiresActive ディレクティブは Expires と Cache-Control ヘッダを有効にするか無効にするかを決めます。Off に 設定された場合はヘッダは生成されません。 On に設定されていれば、ヘッダは ExpiresByType ディレクティブと ExpiresDefault ディレクティブ の基準に従って文書にヘッダを追加します 。

このディレクティブは Expires と Cache-Control ヘッダの存在を保証するわけではなく、基準が満たされていない場合はヘッダは追加されません(指定していないのと同じ結果になります)。

ExpiresDefault ディレクティブ

3行目の ExpiresDefault ディレクティブは全てのドキュメントに対してデフォルトの期限切れ期日を設定します。ExpiresByType ディレクティブによってタイプ毎に上書きすることができます。以下が構文です。

ExpiresDefault <code>seconds
  • <code>
    基準時刻をファイルの最終修正時刻にする(M)か、クライアントのドキュメントへのアクセス時刻にする(A)かを指定します。
  • seconds
    期限切れの日時を生成するための基準時刻に追加される秒数を設定します。

以下は期限切れをクライアントのアクセス時刻から30日(60x60x24x30)に指定する例です。

ExpiresActive On
ExpiresDefault A2592000

ExpiresDefault ディレクティブは全てのファイルを対象にしますが、以下のように FilesMatch ディレクティブを使用して拡張子で指定することもできます。

<FilesMatch "\.(jpg|jpeg|png|gif|js|css|swf)(\.gz)?$">
  ExpiresDefault A2592000
  # 代替期間指定構文 ExpiresDefault "access plus 1 month"
</FilesMatch>

また、ExpiresDefault ディレクティブでは、ExpiresByType ディレクティブと同じように "代替期間指定構文" を使用することができます。以下が構文です。(詳細は次項を参照ください)

ExpiresDefault "<base> [plus] {<num> <type>}*" 

秒の換算

  • 10分:60 x 10 = 600 seconds
  • 1時間:60 x 60 = 3600 seconds
  • 1日:60 x 60 x24 = 86400 seconds
  • 1週間:60 x 60 x 24 x 7 = 604800 seconds
  • 2週間:60 x 60 x 24 x 14 = 1209600 seconds
  • 1ヶ月(30日):60 x 60 x 24 x 30 = 2592000 seconds
  • 1年:60 x 60 x 24 x 365 = 31536000 seconds

ExpiresByType ディレクティブ

ExpiresByType ディレクティブは指定されたタイプのドキュメント (例 text/html) に対して生成される Expires ヘッダと Cache-Control ヘッダの max-age ディレクティブの値を定義します。以下が構文です。

ExpiresByType MIME-type <code>seconds
  • MIME-type
    キャッシュしたいファイルの種類(MIMEタイプ)を指定します。
  • <code>
    基準時刻をファイルの最終修正時刻にする場合は M を、クライアントのドキュメントへのアクセス時刻にする場合は A を指定します。
  • seconds
    期限切れの日時を生成するための基準時刻に追加される秒数を設定します。Cache-Control: max-age は期限切れの時刻からリクエスト時刻を引いたものを秒で表すことで生成されます。

以下は上記構文を使った例です。

ExpiresActive On
# gif ファイルはアクセス日時から 1 ヶ月間有効にします
ExpiresByType image/gif A2592000
# HTML ファイルは最終更新日時から 1 週間有効にします
ExpiresByType text/html M604800

代替期間指定構文(Alternate Interval Syntax)

ExpiresDefault ディレクティブと ExpiresByType ディレクティブは 以下の構文を使って定義することができます。

ExpiresDefault "<base> [plus] {<num> <type>}*"
ExpiresByType type/encoding "<base> [plus] {<num> <type>}*"
base
  • access:基準時刻をクライアントのドキュメントへのアクセス時刻にする場合
  • now ('access' と等価)
  • modification:基準時刻をファイルの最終修正時刻にする場合
plus
plus キーワードは省略可能です。
num
整数値
type
以下のいずれかを指定します。(複数形、単数形どちらでも大丈夫です)
  • years
  • months
  • weeks
  • days
  • hours
  • minutes
  • seconds

以下の例は、全てのドキュメントに対して 1 ヶ月後に期限が切れるようにしています。

ExpiresActive On
ExpiresDefault "access plus 1 month"
ExpiresDefault "access plus 4 weeks"
ExpiresDefault "access plus 30 days" 

期限切れ時刻は <num> <type> を追加することでより細かく制御することができます。

ExpiresActive On
ExpiresByType text/html "access plus 1 month 15 days 2 hours"
ExpiresByType image/gif "modification plus 5 hours 3 minutes" 
Cache-Control ヘッダ

Catch-Control ヘッダの max-age ディレクティブを使ってリソースの有効期間を設定することもできます。Header ディレクティブの引数に set を指定して応答ヘッダを設定します。(Apache モジュール mod_headers

Cache-Control ヘッダは HTTP/1.1 仕様の一部として定義されたもので、レスポンス キャッシュ ポリシーの設定に使用されていた以前のヘッダー(Expires など)よりも優先されます。最新のブラウザはすべて Cache-Control に対応していますが、古いブラウザでは対応していません(かなり少数だと思いますが)。

Cache-Control と Expires を両方記述した場合は、Cache-Control の設定が優先されます。

以下は Catch-Control ヘッダの max-age ディレクティブを使っての設定例です。

FilesMatch ディレクティブを使って、拡張子により、max-age の期間を設定しています。max-age の単位は「秒」で指定します。

3行目は、Connection を指定して接続の持続性(Keep-Alive:持続)を設定しています。

ExpiresActive On
          
Header set Connection keep-alive

<FilesMatch "\.(ico|flv|gif|swf|eot|woff|otf|ttf|svg)$">
  Header set Cache-Control "max-age=2592000, public"
</FilesMatch>

<FilesMatch "\.(jpg|jpeg|png)$">
  Header set Cache-Control "max-age=1209600, public"
</FilesMatch>

<FilesMatch "\.(eot|woff|otf|ttf|svg)$">
  Header set Cache-Control "max-age=2592000, public"
</FilesMatch>

<FilesMatch "\.(js|css)$">
  Header set Cache-Control "max-age=1209600, private"
</FilesMatch>

<FilesMatch "\.(html|php)$">
  Header set Cache-Control "max-age=600, private, must-revalidate"
</FilesMatch>
  • public:どのキャッシュでもレスポンスを保存してよいことを示します。
  • private:レスポンスがひとりのユーザーのためのものであり、共有キャッシュに保存してはならないことを示します。ブラウザのプライベートキャッシュは、この場合でもレスポンスを保存できます。
  • must-revalidate :キャッシュに記録されているコンテンツが現在も有効であるか否かをWebサーバに必ず問い合わせよ、という指示になります。
  • no-cache:キャッシュしたものが有効かどうかサーバでの確認無しにキャッシュを利用してはならないという意味です。名前から勘違いしやすいですが、キャッシュしないという意味ではありません。
  • no-store:キャッシュしないという意味です。
キャッシュを解除する方法

キャッシュの設定を行うと、設定した期間内はブラウザのキャッシュを読み込むようになるため、例えば JavaScript や CSS のファイルを更新しても、新しい内容が反映されないということが発生します。

クエリ文字列を付加する方法

キャッシュを有効にしながら、強制的に新しいファイルを読み込ませる1つの方法は、? から始まる適当なクエリ文字列 ?xxxxx(x は任意の文字)をファイル名の末尾に付加します。

クエリ文字列を変更すると、ブラウザはそのクエリのついた URL をまだ取得していないファイルと認識し、キャッシュは読み込まずにサーバーからファイルを取得します。(実際のファイル名は変更しません)

<link rel="stylesheet" href="../css/style.css?20170812" type="text/css"> 
<script type="text/javascript" src="../js/base.js?date=20170812"></script> 
<img src="images/foo.jpg?20170812" alt="">

通常のクエリ文字列の付けかたは、「?」の後に「name=value」の形式で渡しますが、このようにキャッシュを解除する場合は「name=value」の形式にする必要はありません。

PHP を使用している場合は、変数を設定してそれを参照するようにすると便利です。

<?php $cssVersion = "20170812"; ?>

<link rel="stylesheet" href="style.css?v=<?php echo $cssVersion; ?>">

ファイル名を変更する方法

URL(ファイル名)が変更されると、ブラウザはキャッシュは読み込まずにリソースの再取得を強制されることを利用します。

実際にファイル名を変更するのではなく、HTML の読み込み側の URL を変更します。例えば style.css という CSS ファイルがある場合、以下のように style.xxxx.css に変更します。xxxx には任意の数値を指定します。

<link rel="stylesheet" href="css/style.1234.css">

このままでは、style.1234.css というファイルは存在しないので、CSS ファイルが読み込まれません。そこで .htaccess に以下を記述して正しいファイルを読み込むようにします。以下では、拡張子が .js .css .png .jpg .gif .ico のファイルに対応しています。

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif|ico)$ $1.$3 [L]

リクエストされたファイルが存在しない場合、そのファイルが「ファイル名.数値.拡張子」であれば、「ファイル名.拡張子」に書き換えています。

但し、この方法を使うと例えば Dreamweaver の自動補完機能でそのファイルが対象にならないようです。

キャッシュさせない方法

Apache からブラウザにキャッシュさせないように強制するには、.htaccess に以下のように記述します。(参考サイト:How To Prevent HTTP File Caching With .htaccess

FileETag None
Header unset ETag
Header set Cache-Control "max-age=0, private, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"

特定のファイルを対象にする場合は、以下のように FilesMatch ディレクティブを使って、ファイルの拡張子を指定します。

<FilesMatch "\.(html|php|jpg|jpeg|gif|png)$">
  FileETag None
  Header unset ETag
  Header set Cache-Control "max-age=0, private, no-cache, no-store, must-revalidate"
  Header set Pragma "no-cache"
  Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</FilesMatch> 

FileETag None:
ETag を付けないように設定しています。

Header unset ETag:
Header ディレクティブを使って ETag 応答ヘッダを削除しています。

Header set Cache-Control "max-age=0, private, no-cache, no-store, must-revalidate":
Catch-Control ヘッダを使って、 no-store(キャッシュしない)を指定しています。その他の指定は以下を参照ください。

Header set Pragma "no-cache":
HTTP/1.1 の Cache-Control ヘッダを解さないプロキシが存在するかもしれないので、HTTP/1.0 用に Pragma ヘッダに no-cache を指定しています。

Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT":
期限に過去の日時を指定しています。

  • private:レスポンスがひとりのユーザーのためのものであり、共有キャッシュに保存してはならないことを示します。ブラウザのプライベートキャッシュは、この場合でもレスポンスを保存できます。
  • must-revalidate :キャッシュに記録されているコンテンツが現在も有効であるか否かをWebサーバに必ず問い合わせよ、という指示になります。
  • no-cache:キャッシュしたものが有効かどうかサーバでの確認無しにキャッシュを利用してはならないという意味です。名前から勘違いしやすいですが、キャッシュしないという意味ではありません
  • no-store:キャッシュしないという意味です。

参考サイト:プロキシキャッシュ対策(ipa.go.jp)

HTML の場合、以下のように head 内の META タグに cache-control や Expires を指定する方法があります。但し、ブラウザによって動作が異なったり、ネットワーク上のプロキシなどの関係でうまくキャッシュを制御することができないようです。

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

キャッシュがされていないかを調べるには、ネットワークで HTTPステータスコードが 304 Not Modified ではなく 200 OK になっているかを確認します。

FireFox の言語設定の例

FileETag ディレクティブ

ETag(Entity Tag)は、サーバー上にあるファイルとブラウザ側にあるキャッシュ内のファイルが一致するかどうか判断するためのタグのことです。

ブラウザからは If-None-Match タグにこの値が付けられ、サーバ側ではこの値と ETag の値が異なればレスポンスコード 200(OK)でコンテンツを送信します。同一ならば 304(変更なし)として処理し、キャッシュを使用することで、キャッシュの効率化、および回線帯域の節約ができるようになります。

FileETag ディレクティブは、 ETag 応答ヘッダフィールドを作成するときに使用するファイルの属性を設定します。(Apache コア機能 FileETag ディレクティブ

Apache 1.3.22 以前では、ETag の値は常にファイルの inode, サイズ、最終修正時刻 (mtime) から作成されていましたが、FileETag ディレクティブにより、これらのどれを使うかを選択できるようになりました。以下が認識されるキーワードです。

  • INode:ファイルの inode 番号を計算に使います
  • MTime:ファイルの最終修正時刻を使います
  • Size:ファイルの中身のバイト数を使います
  • All:使用可能なすべてのフィールドを使います。(FileETag INode MTime Size と等価)
  • None:ドキュメントがファイルに基づいたものでも、ETag フィールドを 応答に付加しません

但し、負荷分散のために Web サーバが複数台ある場合は、ファイルが同一でも inode がサーバ毎に異なるため 200 を返す可能性があります。 そのような場合は inode を使わないようにするか、最初から検証をしないように設定した方がいい場合もあるようです。(ブラウザキャッシュでパフォーマンス向上―負荷分散装置の落とし穴に注意

Apache 1.3.23以降では、サーバ側で ETag を付けないことにより、ETag による比較を行わなくすることが可能です。.htaccess に以下を記述します。

FileETag None

Header ディレクティブを使って ETag 応答ヘッダを削除するには、以下のように記述します。(Apache モジュール mod_headers

Header unset ETag
FileETag None

gzip 圧縮での高速化

gzip 圧縮とは、ファイルを「圧縮」する方法の1つです。ファイルを圧縮してサイズを小さくすることでブラウザと Web サーバ間での通信データ量を減らし、ページの表示がより高速になります。

gzip 圧縮の対象は、HTML, CSS, Javascript などのテキストファイルです。画像や pdf などの圧縮済みファイルを gzip 圧縮しても効果がないだけではなく、ファイルサイズが増えて逆効果になる場合もあります。

gzip 圧縮での高速化を行う場合、「自分でファイルを圧縮する方法」と「サーバーでファイルを圧縮する方法(mod_deflate/mod_filter の利用)」があります。

自分でファイルを圧縮する方法

この方法は、静的なウェブのあまり変更のないファイル(HTML, CSS, Javascript)に対して向いています。PHP などで動的にファイルが生成される場合、前もってファイルを gzip で圧縮できないので、「サーバーでファイルを圧縮する方法(mod_deflate の利用)」を使います。

この方法の場合、 gzip に対応していないブラウザを考慮して、通常のファイル(例 style.css や base.js など)と圧縮済みのファイル(例 style.css.gz や base.js.gz など)の2種類のファイルをサーバー上に用意します。gzip 圧縮したファイルの拡張子は「.gz」になります。

gzip ファイルの作り方

gzip 圧縮するには、専用のソフト(Windows の場合:7zip や Lhaplus 等)を使うか、HTML, CSS, Javascript を圧縮するなら次のオンラインツールを利用できます。

Online JavaScript/CSS/HTML Compressor

上記オンラインツールを利用する場合は、そのページの「Input」の入力エリアにファイルの内容をペーストするかファイルをドラッグして、該当するファイルタイプ(Javascript/CSS/HTML)のボタンをクリックして、まずミニファイ化します。その後ファイル名を指定して「gzip」ボタンをクリックすると、gzip 圧縮したファイルをダウンロードできます。

gzip 圧縮オンラインツールの使用例

ファイルを gzip 圧縮すると、ファイルの拡張子は「.gz」になるのでこれをサーバーにアップロードします。例えば、「style.css」を圧縮すると「style.css.gz」になり、サーバー上には「style.css」と「style.css.gz」の2種類のファイルが存在することになります。

HTML タグの変更は不要

link 要素や script 要素の記述は、gzip 圧縮していない(.gz の付いていない)ファイルを指定します。ファイルの読み込みの HTML タグの記述は変更する必要はありません。

<link rel="stylesheet" href="css/style.css">
<script type="text/javascript" src="js/base.js"></script>  

.htaccessに記述を追加

作成した gzip ファイルの転送を有効にするには、.htaccess に以下を記述します。gzip 圧縮に対応しているブラウザには gzip ファイルを、対応していないブラウザには圧縮されていない通常のファイルを転送する設定です。

RewriteEngine On
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME} !\.gz$
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteCond %{REQUEST_FILENAME} (\.js|\.css|\.html)$
RewriteRule .+ %{REQUEST_URI}.gz [L]
     
<FilesMatch "\.js\.gz$">
  ForceType application/x-javascript
  AddEncoding x-gzip .gz  
</FilesMatch>

<FilesMatch "\.css\.gz$">
  ForceType text/css
  AddEncoding x-gzip .gz
</FilesMatch>

<FilesMatch "\.html\.gz$">
  ForceType text/html
  AddEncoding x-gzip .gz
</FilesMatch>
RewriteEngine On
mod_rewrite の機能の On/Off を指定します。URL の書き換え処理を行うには、On にします。
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond で「gzipが許容されていれば」という条件を設定します。
RewriteCond %{REQUEST_FILENAME} !\.gz$
リクエストされたファイルの「拡張子が .gz でなければ」という条件です。
RewriteCond %{REQUEST_FILENAME}\.gz -s
「ファイルの末尾に .gz を付与したファイルが存在すれば」という条件です。
RewriteCond %{REQUEST_FILENAME} (\.js|\.css|\.html)$
「ファイルの拡張子が .js または .css または .html の場合は」という条件です。
RewriteRule .+ %{REQUEST_URI}.gz [L]
RewriteRule で上記の全ての条件を満たす場合、リクエストされた URI の末尾に .gz を追加(書き換え)します。

<FilesMatch "\.js\.gz$">~</FilesMatch> などの FilesMatch を使って拡張子が合致するファイル毎に MIME-TYPE と gz エンコードを指定します。

ForceType ディレクティブは、マッチするファイルが指定の MIME コンテントタイプ(Content-Type)で送られるようにします。(Apache コア機能:ForceType ディレクティブ

AddEncoding ディレクティブは、与えられた拡張子を指定されたエンコーディングに関連付け(マップ)します。「AddEncoding x-gzip .gz」は拡張子 .gz を含むファイル名が x-gzip エンコーディングを使ってエンコードされていること指定します。(Apache モジュール mod_mime:AddEncoding ディレクティブ

gzip 圧縮で通信されていることの確認

FireFox の FireBug や Chrome のデベロッパーツール等の Network でレスポンスヘッダの情報を確認して、Content-Encoding が gzip になっていれば、gzip 圧縮で通信されています。

gzip 圧縮で通信されていることの確認(FireFoxの例)

ブラウザが圧縮ファイルをサポートしている場合、HTTP リクエストに「Accept-Encoding: gzip, deflate」を付加して、gzip 圧縮や deflate 圧縮ファイルを受けつけられることをサーバに伝えます。

このリクエストを受信したサーバは、圧縮ファイルを返せる場合、「Content-Encoding: gzip」を付与したレスポンスと圧縮したファイルを返します。

gzip 圧縮で通信されていることの確認(Chromeの例)

mod_deflate の利用

mod_deflate モジュールを利用すると、サーバーが自動で gzip 圧縮をしてくれますが、リクエストがある度にサーバーが圧縮をおこなうため、圧縮コストがかかります(サーバのCPU消費率が高くなります)。レンタルサーバによってはこの機能を提供していない場合もあるので、使用しているサーバーが「mod_deflate」をサポートしているかを確認する必要があります。

mod_deflate モジュールがサポートされ機能しているかどうかを調べるには、以下のサイトにアクセスして、「Enter a URL to Test」に、目的のサイトの URL を入力して「Test」をクリックします。

http://www.whatsmyip.org/http-compression-test/

以下のように表示されれば、圧縮の機能がサポートされています。

mod_deflate モジュールがサポートされているかを確認するサイトでの実施例

特定の MIME タイプについてのみ圧縮したいのであれば、 AddOutputFilterByType ディレクティブを使用します。(Apache モジュール mod_deflate

text, html, xml, css, javascript を圧縮して転送するには、以下を .htaccess に記述します。mod_deflate の機能が有効な場合は、IfModule ディレクティブは不要です。

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/text text/html text/plain text/xml text/css application/x-javascript application/javascript
</IfModule>

XAMPP の場合

ローカル環境で XAMPP を使っている場合に、この機能を有効にするには、httpd.conf(デフォルトでは、C:\xampp\apache\conf)の以下のラインのコメント(#)を外します。

LoadModule deflate_module modules/mod_deflate.so
LoadModule filter_module modules/mod_filter.so

以下は「SetOutputFilter DEFLATE」を設定して、画像(gif,jpg,jpeg,png)以外の全てのファイル圧縮する例です。「Apache モジュール mod_deflate」に記載されているものを使っています。

SetOutputFilter DEFLATE
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
# BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
Header append Vary User-Agent env=!dont-vary 
SetOutputFilter DEFLATE
このディレクティブのあるコンテナ中のドキュメントを全て圧縮するようにします
2行目~4行目
BrowserMatch ディレクティブを使っての圧縮に問題を抱えている古いブラウザへの対処です。(これらは古いブラウザなので、コメントアウトまたは削除しても問題が少ないと思われます)
SetEnvIfNoCase
SetEnvIfNoCase ディレクティブを使って画像を圧縮の対象外にしています。(Apache モジュール mod_setenvif:SetEnvIfNoCase ディレクティブ
Header append Vary
適切な Accept-Encoding リクエストヘッダを送信するクライアントに対してのみ、 プロキシサーバがキャッシュした応答を送信するようにして、圧縮を扱うことのできないクライアントに 圧縮された内容が送られることのないようにします。「Header append Vary Accept-Encoding env=!dont-vary」として、User-Agent ではなく、Accept-Encoding にした方が効率が良いとのことです。(未確認)

mod_filter の利用

以下は mod_filter モジュールを利用して html, css, xml, javascript, json 等のファイルを圧縮する例です。Apache のバージョンが 2.4.4 未満と以降では FilterProvider の書き方(構文)が異なるので、IfVersion ディレクティブで分岐しています。(Apache フィルタ

# Apache2.4.4 未満
<IfVersion < 2.4.4>
<IfModule filter_module>
FilterDeclare   COMPRESS
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/html
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/css
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/plain
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/xml
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/x-component
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/javascript
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/json
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xml
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xhtml+xml
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/rss+xml
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/atom+xml
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/vnd.ms-fontobject
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/svg+xml
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/x-font-ttf
FilterProvider  COMPRESS  DEFLATE resp=Content-Type $font/opentype
FilterChain     COMPRESS
FilterProtocol  COMPRESS  DEFLATE change=yes;byteranges=no
</IfModule>
</IfVersion>

# Apache2.4.4 以上
<IfVersion >= 2.4.4>
<IfModule filter_module>
FilterDeclare   COMPRESS
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'text/html'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'text/css'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'text/plain'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'text/xml'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'text/x-component'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'application/javascript'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'application/json'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'application/xml'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'application/xhtml+xml'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'application/rss+xml'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'application/atom+xml'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'application/vnd.ms-fontobject'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'image/svg+xml'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'image/x-icon'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'application/x-font-ttf'"
FilterProvider  COMPRESS  DEFLATE "%{Content_Type} = 'font/opentype'"
FilterChain     COMPRESS
FilterProtocol  COMPRESS  DEFLATE change=yes;byteranges=no
</IfModule>
</IfVersion> 

上記は「htaccess FilterProvider giving error 500」や「FilterProvider syntax changed in Apache 2.4 」を参考にしています。

現時点では、2.4.4 以上のコードは試せていません。

FilterDeclare ディレクティブでは、出力フィルタを指定します。以下が構文です。(Apache Module mod_filter:FilterDeclare Directive

FilterDeclare filter-name [type]
filter-name
FilterProvider、FilterChain、FilterProtocol ディレクティブで使うフィルタ名を指定します。
[type](オプション)
RESOURCE、CONTENT_SET、PROTOCOL、TRANSCODE、CONNECTION、NETWORK のいずれかを指定します。省略した場合は、デフォルトの RESOURCE になります。

FilterProvider ディレクティブは、スマートフィルタのプロバイダを登録します。Apache のバージョンが 2.4.4 未満と以降では書き方(構文)が異なります。

Apache Module mod_filter:FilterProvider(2.2)

プロバイダ(上記の例では DEFLATE)は match の文字列が、dispatch として宣言されたヘッダーや環境変数の値と一致した場合のみ呼び出されます。

dispatch は、req(リクエストヘッダ) 、resp(レスポンスヘッダ)、env(環境変数) を意味するプレフィックスを設定できます。省略した場合のデフォルトは、resp(レスポンスヘッダ)になります。

match は、dispatch で指定したプレフィックスの設定値と比較するための(マッチする)文字列を指定します。

# Apache2.4.4 未満
FilterProvider filter-name provider-name [req|resp|env]=dispatch match 

Apache Module mod_filter:FilterProvider(2.4)

プロバイダは expression の式が true の場合にのみ呼び出されます。

# Apache2.4.4 以上
FilterProvider filter-name provider-name expression

MultiViews で多言語対応

HTTP/1.1では、ユーザーエージェント(UA)のヘッダに含まれるメタデータを判別して、(メディアタイプ、言語、文字セット、エンコーディングなどの優先傾向に基づいて)複数のリソースの中から最適なものを選択するためのコンテント・ネゴシエーションの仕組みが定められています。

例えばユーザーエージェントから送信される Accept-Language ヘッダ値に「en」が優先されていれば英語版のファイル、「ja」が優先されていれば日本語版のファイルが送り出されるようになります。

但し、言語の数が多かったりディレクトリ内にファイルやリソースが多い場合などは、適切な言語を見つけるのに時間がかかることがあり、そのような場合は、MultiViews の代わりにタイプマップ(type-map ハンドラ)を使用する方が良いとのことです。(Apache コンテントネゴシエーション / Apache モジュール mod_negotiation

MultiViews

Apache には、コンテント・ネゴシエーションを簡単に利用できるようにするための MultiViews という機能が用意されています。

MultiViews はディレクトリ毎のオプションで、httpd.conf や .htaccess で Options ディレクティブによって設定することができますが、Options All が指定されていても MultiViews 機能は有効にならないので明示的に指定する必要があります。

以下のように記述することで MultiViews を有効にすることができます。

Options +MultiViews

MultiViews を利用した言語ネゴシエーション

例えば、日本語と英語の2ヶ国語の場合、以下のように記述して言語コードと拡張子を関連付け(マッピング)します。

AddLanguage ja .ja
AddLanguage en .en

上記の設定により、.ja という拡張子を持つファイルは日本語に、.en という拡張子を持つファイルは英語に関連付けられます。

そしてサーバー上に、日本語版と英語版のファイルを以下のような拡張子を加えたファイル名で用意します。

  • index.html.ja
  • index.html.en

例えば、上記のファイルを http://example.com のトップディレクトリに配置し場合、http://example.com/ または http://example.com/index.html でアクセスすると、ブラウザの言語設定の優先順位により、その言語のファイルが表示されます。以下は FireFox の言語設定ですが、この場合は「英語(en)」が優先順位が高いので、英語版が表示されることになります。

FireFox の言語設定の例

AddLanguage ディレクティブ

AddLanguage ディレクティブは、拡張子(extension)に指定された言語(MIME-lang)を関連付けます。以下が構文です。

AddLanguage MIME-lang extension [extension] ...

MIME-lang は、拡張子(extension)を含んでいるファイル名の MIME における言語です。新しい関連付(マッピング)は既存の関連付に追加され、同じ拡張子(extension)の関連付を上書きします。また、引数の extension は大文字小文字を区別せず 最初のドット(.)は省略できます。

以下の場合、xxxx.en.Z ドキュメントは圧縮(compress)された英語のドキュメントとして扱われます (xxxx.Z.en も同様)。

AddEncoding x-compress .Z
AddLanguage en .en
AddLanguage fr .fr 

また、複数の言語が同じ拡張子に割り当てられているときは、最後のものが使用されます。以下の場合、拡張子 .en のあるドキュメントは en-us として扱われます。

AddLanguage en .en
AddLanguage en-gb .en
AddLanguage en-us .en 

デフォルト言語ファイルの指定

設定した言語がユーザーのブラウザ(UA)の言語設定にない場合や、コンテントネゴシエーションをサポートしていないブラウザ(UA)で HTTP 406 (NOT ACCEPTABLE) エラーにならないようにするために、デフォルトの言語を設定します。

Apache 2.0.30 以上では、LanguagePriority ディレクティブと ForceLanguagePriority ディレクティブを使ってデフォルトの言語を設定することができます。

LanguagePriority ディレクティブは MultiViews リクエストを扱うときに、クライアントが言語の優先順位を提供していない場合に言語の優先順位を設定します。以下が構文です。

LanguagePriority MIME-lang [MIME-lang] ...

言語キーワード(MIME-lang)を、優先度の高い順に記述します。このディレクティブは、ForceLanguagePriority ディレクティブが None 以外(Prefer または Fallback またはその両方)のときにのみ有効になります。

ForceLanguagePriority ディレクティブはリクエストに合うドキュメントを一つだけ返すことができないときに、 LanguagePriority ディレクティブを使ってネゴシエーションの結果を返します。以下が構文です。

ForceLanguagePriority None|Prefer|Fallback [Prefer|Fallback]
ForceLanguagePriority に設定できる値
説明
None ForceLanguagePriority ディレクティブの機能を無効にします。
Prefer 同等の選択肢がいくつかあるときに、HTTP の 300 (MULTIPLE CHOICES) を返す代わりに、 LanguagePriority を使って一つだけドキュメントを返すようにします。ユーザの Accept-Language ヘッダの言語の優先順位が同じ場合は、LanguagePriority の中の最初にマッチする言語のコンテンツを返します。
Fallback ユーザの Accept-Language ヘッダに指定された言語のコンテンツが存在しない場合は、HTTP 406 (NOT ACCEPTABLE) を送信する代わりに、 LanguagePriority のリストの最初の言語のコンテンツを返します。
Prefer と Fallback Prefer と Fallback の両方のオプションを同時に指定すると、複数の候補があるときは LanguagePriority の最初の候補が送られ、クライアントの許容言語に合う候補がないときは 存在するドキュメントで最初のものが送られれます。

2ヶ国語対応の例

以下のように「multiviews」と言うディレクトリに、.htaccess, index.html.en, index.html.ja, en ディレクトリ, ja ディレクトリ を配置した構成になっています。

テストのフォルダ及びファイルの構成図

  • index.html.en:英語のファイル
  • index.html.ja:日本語のファイル
  • en ディレクトリ:英語コンテンツを収容するディレクトリ(sample.html を配置)
  • ja ディレクトリ:日本語コンテンツを収容するディレクトリ(sample.html を配置)

以下を .htaccess に記述します。AddLanguage ディレクティブでは、通常の en と ja の他に、ja-jp, ja-jp-mac, en-us を指定しています。

Options +MultiViews

AddLanguage ja-jp-mac .ja
AddLanguage ja .ja
AddLanguage ja-jp .ja
AddLanguage en-us .en
AddLanguage en .en

LanguagePriority en ja
ForceLanguagePriority Prefer
ForceLanguagePriority Fallback

multiviews ディレクトリへのアクセスは、http://multiviews/ でアクセスすると仮定します。

  • http://multiviews/ または
    http://multiviews/index.html (言語の拡張子なし)でアクセスすると、優先度の高い言語のファイルが表示されます。

  • http://multiviews/index.html.ja や http://multiviews/index.html.en でアクセスすると、それぞれの言語のファイルが表示されます。

  • sample.html からトップページへのリンクは ../index.html でアクセスできます。

  • それぞれのトップページから多言語へのトップページへのリンクは言語の拡張子をつける必要があります。単に index.html では、自分のページへ戻ってしまいます。

実用的ではない例

前述の構成に、言語拡張子の付いていない index.html を追加するとどうなるかを確認する例です。(あまり意味はありません)。以下のような構成になります。

言語拡張子の付いていない index.html を追加した場合のテストのフォルダ及びファイルの構成図

この場合、前述の例とは異なる結果になります。

  • http://multiviews/ または
    http://multiviews/index.html (言語の拡張子なし)でアクセスすると、index.html (言語の拡張子なし)が表示されます。

  • http://multiviews/index.html.ja や http://multiviews/index.html.en でアクセスすると、それぞれの言語のファイルが表示されます。

  • sample.html から「../index.html」のリンクでアクセスすると、いずれの場合も index.html (言語の拡張子なし)が表示されます。

.htaccess に DirectoryIndex index という1行を追加してみます。以下のようになります。

Options +MultiViews
AddLanguage ja-jp-mac .ja
AddLanguage ja .ja
AddLanguage ja-jp .ja
AddLanguage en .en
AddLanguage en-us .en

LanguagePriority en ja
ForceLanguagePriority Prefer
ForceLanguagePriority Fallback

DirectoryIndex index

.htaccess を上記のように設定すると、動作が変わります。

  • http://multiviews/ でアクセスすると、優先度の高い言語のファイルが表示されます。

  • http://multiviews/index.html (言語の拡張子なし)でアクセスすると、index.html (言語の拡張子なし)が表示されます。

  • http://multiviews/index.html.ja や http://multiviews/index.html.en でアクセスすると、それぞれの言語のファイルが表示されます。

  • sample.html から「../index.html」のリンクでアクセスすると、いずれの場合も index.html (言語の拡張子なし)が表示されます。

何回か繰り返して試したところ、ローカル環境では問題がありませんが、実際のサーバーで試すと、時々アクセスに長い時間がかかる場合がありました。また、キャッシュが影響するので、キャッシュしない設定も .htaccess に追加する必要がありました。

以下は多言語対応の参考サイトです。