[ホーム] -> [Aache + PHP + PostgreSQL 実験室] |
Apache の基本的な設定についてはすでに説明しましたが、もう少し説明しておいた方がいい項目があるので、ここで説明しておきましょう。もちろん、すべてを説明するには無理がありますので、独断と偏見で抜粋してあります。
Apache の設定項目は、「ディレクティブ」とか呼ばれています。これは英語の名称から来ているのですが。各ディレクティブの値にスペースが入っている場合は、ダブルクォーテーション「"
」で囲む必要があります。また、設定値が長くて2行以上に分けたい場合は、改行の手前にバックスラッシュ「\
」を置くことにより、複数行に分けることが出来ます。
前にした説明と重複する項目もありますが、こちらをリファレンス的に参照できるようにと考えています。機能別に振り分けましたので、それぞれを参照してみてください。
Apache の設定の中には、範囲を指定して、その範囲に対して設定を有効にするためのディレクティブがあります。このディレクティブだけで利用することは出来ず、他のディレクティブと組み合わせて使うのですが、うまく使うと大変便利です。
サーバプログラム自体の動きを設定します。Port を何番でサービスを開始するかや、サーバプログラムの実行権限を決めたりします。変更する必要がなくても、必ず目を通してください。
サーバが扱うオブジェクト(html ファイルや、CGI プログラム)全体の設定をここでします。ドキュメントを格納するディレクトリの設定や、ファイル名を省略してアクセスした際の、デフォルトのファイル名や、果てはサーバエラーの時のフォームを設定したりします。
主に、ドキュメントのセキュリティに関する設定を行います。どのホストからの接続を許可するかや、パスワードを入力しないかぎりアクセスできなくさせたりできます。このファイルでは、これらの設定をディレクトリ単位で指定できます。
ディレクティブでも説明しましたが、アクセス制御についてまとめてみます。
アクセス制御に関しては大きく二種類有り、アドレス認証と、ユーザ認証です。アドレス認証は、クライアントのアドレス(IP アドレス、ホスト名等)を元に制御するに対し、ユーザ認証はユーザ名とパスワードを入力してもらい、その情報を元に認証を行います。
アドレス認証は、次のように Order, Allow, Deny
ディレクティブを使います。
# ドメインが domain.com からのみ許可 Order Deny,Allow Deny from all Allow from .domain.com # この例は、見たとおりに一部から許可だとわかりやすい。 # これも同じで domain.com からのみ許可 Order Allow,Deny Allow from .domain.com # この例はデフォルト Deny に頼っている。 # domain.com は許可だけど、host1.domain.com は拒否 Order Allow,Deny Allow from .domain.com Deny from host1.domain.com # この例はデフォルト Deny に頼っている。 # ただし、複雑な指定はしやすい。 # domain.com は拒否だけど、host1.domain.com は許可 Order Deny,Allow Deny from .domain.com Allow from host1.domain.com
一部だけに許可し、さらにその中の一部を拒否したい場合は Allow,Deny
、一部だけ拒否し、さらにその中の一部を許可したい場合は Deny,Allow
を使います。例の上の二つのようにどちらでも表現できるものもありますが、どちらを使うかは管理者の好みなのでしょう。
ユーザ認証は、次のようにパスワードファイルを用意して行います。
# これはすべてで必要 AuthAuthoritative on AuthType Basic AuthName "Sample Authenticator" # users.dat にあり、パスワードが正しければ全員許可 AuthUserFile /www/data/users.dat Require valid-user # users.dat にあり、パスワードが正しく、指定されユーザのみ許可 AuthUserFile /www/data/users.dat Require user adminuser # users.dat にあり、パスワードが正しく、 # そのユーザが groups.dat の該当グループにいれば許可 AuthUserFile /www/data/users.dat AuthGroupFile /www/data/groups.dat Require group admingroup
また、アドレス認証とユーザ認証を組み合わせることも出来ます。
# .domain.com から許可するが、それ以外は users.dat にある、 # ユーザ認証をする必要がある Satisfy Any Order Deny,Allow Deny from all Allow from .domain.com AuthAuthoritative on AuthType Basic AuthName "Sample Authenticator" AuthUserFile /www/data/users.dat Require valid-user # users.dat にあるユーザ認証をする必要があるが、 # .dame.com からはアクセスできない Satisfy All Order Deny,Allow Deny from .dame.com AuthAuthoritative on AuthType Basic AuthName "Sample Authenticator" AuthUserFile /www/data/users.dat Require valid-user
組み合わせの場合、アドレス認証が先に行われるので、Satisfy All
で、拒否されるアドレスからのアクセスの場合は、基本認証は行われません。
Apache のログには、エラーログとアクセスログがあります。
エラーログは ErrorLog
ディレクティブで設定したファイルに出力されるログで、LogLevel
により、出力されるログの種類が決定されます。このログファイルには、サーバ起動・終了した、ファイルが見つからないなどのエラーが記録されます。管理者は定期的に見るといいですが、ゴミも多いので、見るときはフィルターをかけるなど工夫してください。
もう一つのアクセスログは、いろいろカスタマイズできるので、ここで説明しておきます。カスタマイズ対象は、大きく二つあり、ログのフォーマットと、出力先です。ファイルに保存して、ログ解析ツールなどを利用する場合は、あまりフォーマットは変更せず、すでに定義済みの combined
などを使うといいと思います。ここでは、ログを PostgreSQL に記録する説明をします。
概要は、Apache が出力するログのフォーマットを insert
文にし、その出力を psql
に渡して insert
を実行する、というものです。
まず、ログのフォーマットですが、LogFormat
ディレクティブで設定します。マニュアルを見るといろいろな書式が使えることが分かります。ここでは、PostgreSQL に記録できるようにするために、次のようにします。
LogFormat "insert into apache_log values \ ('%h','%>l','%u','%{%Y-%m-%d %H:%M:%S %Z}t','%r',%>s,%B,\ '%{Referer}i','%{User-Agent}i')" \ pgsql
これでログの出力フォーマットが insert
文になりました。次に CustomLog
ディレクティブを利用して、この insert
文を psql
に流すようにします。
CustomLog "| sed -n 's/;/\\\\;/g; p' \ |/usr/local/pgsql/bin/psql -q -S wwwdb nobody" pgsql
wwwdb
がデータベース名で、nobody
がデータベースに接続するユーザ名です。ご自分の環境に合わせて修正してください。この方法だと、psql
がパスワードを聞いてくる場合はうまくいきません。pg_hba.conf
を修正して、パスワードなしでも接続できるように設定をゆるめる必要があります。
その前の sed
は、アクセス文字列に不正な文字があった場合の最低限の対処です。もしこれがないと、別の SQL 文を実行することが可能になってしまいます。この設定をしていても、シングルクォーテーション「('
」が入っていると、ログに記録されません。また、sed
が出力をバッファリングするようで、ある一定以上ログがたまらないと psql
にデータを渡してくれません・・・。
設定はこれだけなので、apachectl restart
として再起動してみましょう。もし何かエラーがれば、error_log
に記録されているはずです。
特にエラーが出ていないようでしたら、何かページにアクセスしてみて、うまくテーブルにデータが追加されているか確認しましょう。バッファリングされているので、何度も何度も(数十回)アクセスしてみてください。一気にログに出力されるはずです。
セキュリティを考慮したとき、危険な文字列は「');
」という文字列です。この文字の後ろに SQL 文を書かれると、それが実行されています。上の sed
でセミコロン「;
」だけ対処して、複数の SQL 文が実行されないようにしています。本当は、シングルクォーテーションをエスケープするのが正解ですが、もっと複雑な仕組みが必要なので、やめにしました。ちなみに、「ブラウザからはこの文字列は投げられない」と思った人は不正解です。別にブラウザを使わなくても、直接ポート80に投げれば(telnet
とか使って)どんな文字でも遅れます。400 Bad Request が返ってきますが、結局その文字列自体がログに記録されてしまうので、サーバ側で何か対処しざるを得ません。
もし、上記の sed
対応ではまずいと思った人や、別の方法を知っている方がいらっしゃったら、ご教授ください。
Apache の機能の一つに、仮想ホスト(バーチャルホスト)の機能があります。これは、一つのサーバで、複数のサーバが動いているように見せかける機能です。例えば、DNS の設定で同じ IP アドレスに対し、www.foo.com
と www.bar.com
の二つの名前を付けたとします(A
でも CNAME
でも)。このとき、特に設定しないと /index.html
へのアクセスはどちらの名前でアクセスしても同じファイルを参照します。しかし、仮想ホストの機能を使えば、これを別々に出来ます。
仮想ホストには名前ベースと IP ベースの二種類あります。まずは IP ベースの設定をしてみましょう。
IP ベースとは、一つのマシンに複数の IP アドレスを持っていて、それぞれで別々の設定を行う場合に使います。例えば 192.168.0.51
と 10.3.0.11
という IP アドレスを持っていたとしましょう。httpd.conf
に次のように設定します。
<VirtualHost 10.3.0.11> ServerName project.foo.com DocumentRoot /www/project </VirtualHost> <VirtualHost 192.168.0.51> ServerName personal.foo.com DocumentRoot /www/personal </VirtualHost>
これで、それぞれ別の設定になりました。ここでは ServerName, DocumentRoot
ディレクティブしか設定していませんが、他のディレクティブを指定して、ログファイルを分けたりとか、アクセス制御を分けたりとか出来ます。IP アドレスの部分が _default_
の仮想ホスト(<VirtualHost _default_>
)を設定でき、アクセスが他の仮想ホストの IP アドレスにマッチしない場合、この _default_
ホストの設定が利用されます。つまり、この例の場合は IP アドレスを 3 つ以上持っていた場合に _default_
の設定が利用されます。もし _default_
ホストが無いと、大元の設定(つまり httpd.conf
で普通に設定したやつ)が利用されます。
次は名前ベースの仮想ホストです。こちらは、一つの IP アドレスに対して複数の名前が付いているとき、設定を分けるのに利用します。アクセスされた IP アドレスではなく、アクセスするときに利用したホスト名が利用されるのです。これは次のようにします。
NameVirtualHost * <VirtualHost *> ServerName opaq.foo.com DocumentRoot /www/opaq </VirtualHost> <VirtualHost *> ServerName tepco.foo.com DocumentRoot /www/tepco </VirtualHost>
NameVirtualHost
ディレクティブの設定は必須です。アクセスするときに利用したホスト名が ServerName
に指定した値と同じ場合に、その仮想ホストの設定が利用されます。もし、一つの仮想ホストに複数の名前を割り当てたい場合は、ServerAlias
ディレクティブを利用します。
NameVirtualHost * <VirtualHost *> ServerName project.foo.com ServerAlias internal.foo.com DocumentRoot /www/project </VirtualHost>
定義されている名前以外でアクセスがあると、一番最初に定義した名前ベースの仮想ホストの設定が利用されます。
NameVirtualHost
ディレクティブは、名前ベースの仮想ホストで利用する IP アドレスを指定します。もし IP アドレスを複数持っていて、IP ベースの仮想ホストと組み合わせたい場合は、*
ではなく、利用する IP アドレスを指定します。例えば次のようになります。
# Host A <VirtualHost _default_> ServerName internal.foo.com DocumentRoot /www/internal </VirtualHost> # Host B <VirtualHost 192.168.0.51> ServerName personal.foo.com DocumentRoot /www/personal </VirtualHost> NameVirtualHost 10.3.0.11 # Host C <VirtualHost 10.3.0.11> ServerName opaq.foo.com DocumentRoot /www/opaq </VirtualHost> # Host D <VirtualHost 10.3.0.11> ServerName tepco.foo.com DocumentRoot /www/tepco </VirtualHost>
この設定で、このマシンが 192.168.0.51, 192.168.0.55, 10.3.0.11, 10.3.0.15
の4つの IP アドレスを持っていて、名前を次のように持っていた場合に利用される仮想ホストの対応表です。
personal.foo.com 192.168.0.51 Host B seep.foo.com 192.168.0.51 Host B calgon.foo.com 192.168.0.55 Host A opaq.foo.com 10.3.0.11 Host C tepco.foo.com 10.3.0.11 Host D sonet.foo.com 10.3.0.11 Host C totte.foo.com 10.3.0.15 Host A
この仮想ホストの設定を確認したい場合は、httpd -S
とコマンドを打ってみてください。仮想ホストの分析結果が表示されます。思った通りに設定できているか確認してみてください。
名前ベースの仮想ホストは、HTTP リクエストヘッダーの Host:
を見て判断しています。古いブラウザなどはこの値を送らないものがあるそうです(お目にかかったことがないので分からない)。もし、Host:
を送ってこないと、どのホスト名でアクセスしようとしても、デフォルトホスト(一番最初に定義した仮想ホスト)の設定になってしまいます。その場合は、ServerPath
ディレクティブを指定します。
ServerName www.foo.com DocumentRoot /www/root NameVirtualHost * <VirtualHost *> ServerName project.foo.com DocumentRoot /www/project ServerPath "/project" </VirtualHost>
この様に設定しておくと、http://www.foo.com/project/
へのアクセスが、http://project.foo.com/
へのアクセスされたのと同じ設定で動作します(仮想ホストの設定が使われる)。つまり、Host:
ヘッダーを送らないブラウザが、http://project.foo.com/
にアクセスすると、http://www.foo.com/
へのアクセスと同じになってしまいますが、そこでそのページに http://www.foo.com/project/
へのリンクが書かれていれば、そちらからアクセスできるようにまります。ただし、この場合気を付けなければならない点が一つあり、project.foo.com
の中で利用しているリンクのパスを、すべて相対パスで指定しないといけない点です。リンクを ../images/log.png
とか指定していればいいですが、/images/log.png
としておくと、http://www.foo.com/project/
からアクセスした場合にリンクが正しくなくなってしまいます。
名前ベースは、かなり便利なので、一度使ってみてください。私は、いろいろなサイト構築の仕事をしたりしますが、それぞれのプロジェクトで開発用のマシンを用意できない場合は、一つのマシンに仮想ホストの設定して開発したりしています。それぞれの仮想ホストでルートディレクトリが変えられるのはありがたいです。
コンテントネゴシエーションとは、簡単に言うとクライアント(ブラウザ)が送ってきた情報を元に、適切なドキュメントを返す仕組みです。コンテントネゴシエーションを有効に知るには、Options +MultiViews
と指定して有効になっていないといけません。どのドキュメントを返すかを判断する材料となるのは、次の4つがあります。
Accept-Language:
)
Accept:
)
Accept-Encoding:
)
Accept-Charset:
)
これらを元に判断するわけですが、これらの機能を使うには、mod_mime モジュールが利用でくるようになっていないといけません。それと、以下の説明で出てくる拡張子についてです。例えば、index.html.ja
というファイルがあったとすると、この拡張子は html
と ja
です。この拡張子の順番には、特にどちらが先でもいいですし、拡張子の数も二つ以上あっても問題ありません。
コンテントネゴシエーションを使ってアクセスする場合は、拡張子を指定せずに(/index.html
ではなく、/index
)アクセスしますが、もし拡張子も指定してアクセスした場合、それ以降の拡張子がコンテントネゴシエーションで使われます。例えば、/index.html
とアクセスした場合、index.html.ja, index.html.en
などが対象になりますが、index.ja.html
は対象になりません。
また、ブラウザが送ってきた条件にあったファイルが見つからないけれど、他の候補がある場合は 406 Not Acceptable が返ってきて、他の候補を選択できる一覧が表示されます。これが返ってきた場合は、ブラウザの条件に合うファイルがなかったことを意味しますので、Apache の設定、ブラウザの設定、用意したファイルの名前を確認してください。
この中で一番よく使われると思うのは言語でしょう。ブラウザは大抵、「言語の優先順位」とか「表示する言語の順序」などの設定が出来ます。ブラウザはこの値を元に、Accept-Language:
という HTTP リクエストヘッダーを生成し、サーバに送ります。サーバ側ではこのヘッダーの値を元に、どのドキュメントを返すか決定します。
Accept-Language:
ヘッダーには、例えば「ja, en;q=0.50, fr;q=0.20
」という風に設定されています。ja, en, fr
は言語名です。各言語名の後ろに付いている、;q=0.50
とかは、優先度です。0
から 1
の値を取り、1
の方が優先度が高いです。省略されている場合は 1
が指定されたのと同じことになります。つまりこの例だと、日本語、英語、フランス語の順番の優先度が付いています。
この状態で、/index
というファイル(拡張子は指定しません!)にアクセスすると、サーバ側では拡張子に言語名の付いたファイルを検索します。index.html.ja, index.ja.html, index.html.en, index.en.html ...
と検索されていき、最初に存在するものを返します。
複数拡張子が付いていても、その中のどれかに当てはまればいいので、.html.ja, .ja.html
のどちらでも問題ありません(両方ある場合は、言語名が最後に付いているファイルが優先されるみたいです)。また、言語を表す拡張子が複数付いていた場合は、どちらの言語を指定されても、そのファイルは対象とまります。
試しに次のようにファイルを作り、ブラウザの言語の設定を変えてアクセスしてみてください。httpd.conf
か .htaccess
に Options +MultiViews
と指定するのを忘れないでください。
> echo Japanese > lang.html.ja > echo English > lang.html.en > echo French > lang.html.fr > echo Unknown > lang.html
というファイルを作り、lang
に(拡張子は指定しません!)アクセスしてみてください。ブラウザの言語の優先順位を変えていろいろアクセスしてみてください。うまくいきましたか? うまくいかない人もいるかもしれます。と言うのは、これ以外でいくつかの設定に原因があるのです。
# 言語名と拡張子を結びつける AddLanguage en .en AddLanguage ja .ja AddLanguage fr .fr # 優先度が設定されていない場合の優先順位 LanguagePriority en ja # 言語名の拡張子が付いていないファイルの言語名 DefaultLanguage en
大きく上記の三つで、利用する言語が AddLanguage
ディレクティブで定義されているか確認してください。最近の Apahce はデフォルトで多くの言語設定がされているので問題ないはずです。LanguagePriority
(mod_negotiation)は、Accept-Language:
ヘッダーに複数の言語が指定されているにもかかわらず、それらの言語に優先度(;q=
)が指定されていない場合に、サーバ側で判断する優先順位です。しばらく前のブラウザ(HTTP/1.1 に沿っていないやつ)は、これの優先度を付けないのがあるので、注意が必要です。DefaultLanguage
は、拡張子に言語名が含まれていない場合に、そのファイルがどの言語のファイルかを指定するものです。
次にファイルタイプですが、文章なら HTML ファイルかテキストファイルか、画像なら PNG か JPEG か GIF かなどを決定するものです。
Apache は、ブラウザが送ってくる Accept:
ヘッダーを見て判断しますが、大抵のブラウザはこの値をカスタマイズすることは出来ません。例えば、Mozilla 1.0 ブラウザは、次のような値を送ります。(適当に改行しています)
Mozilla 1.0 の場合 Accept: text/xml,application/xml,application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8, video/x-mng, image/png,image/jpeg, image/gif;q=0.2, text/css, */*;q=0.1 Internet Explorer 6.0 の場合 Accept: */*
この値を見ると、Mozilla の場合、文章のは XML > HTML > テキスト で、画像は PNG,JPEG > GIF の優先度なのが分かりますね。ちなみに Accept:
ヘッダーで指定されている値は、MIME タイプですので、この MIME タイプに対応する拡張子が登録されていないといけません。TypesConfig
ディレクティブで指定されたファイルに MIME タイプと拡張子の対応が書かれているか確認してください。もし無い場合は追加するか、AddType
ディレクティブを使って定義する必要があります。
# MIME タイプと拡張子を関連付け #(以下の3つは TypesConfig で指定されているはずなので普通はいらない) AddType text/xml .xml AddType text/html .html AddType text/plain .txt # 拡張子がない場合のデフォルトの MIME タイプ DefaultType text/plain
この様な設定で type.xml.en, type.html.en, type.txt.en
というファイルを作って確認してみてください。全部存在すれば type.xml.en
、それがなければ type.html.en
と言う風に表示されるはずです。ちなみに言語の拡張子を指定していますが、この言語拡張子を付けておかないと DefaultLanguage
で指定された言語ファイルだと認識されます。
ファイルタイプを表す拡張子が複数付いていた場合は、右側(後ろ側)の拡張子が優先されます。
エンコーディングは Accept-Encoding:
ヘッダーの値を見て行われます。現在このエンコーディングは、gzip
か compress
による圧縮くらいしか使い道がありません。このヘッダー自体、IE は送ってきませんし、ユーザがこの値を直接設定できるブラウザもあまり無いでしょう。ちなみ Mozilla 1.0 だと、次のようになります(Mozilla は、prefs.js
でこの値を変えられます)。
Accept-Encoding: gzip,deflate,compress,identity
試してみるには、次のようにしてファイルを用意してみましょう。
> echo gzip file | gzip > enc.gz.html.en > echo compress file | compress > enc.Z.html.en > echo normal file > enc.html.en
Apache の設定は次のようになります。最初から指定してある設定は、x-
が付いているので、注意してください。
AddEncoding compress Z AddEncoding gzip gz
この様にして、enc
にアクセスすると、enc.gz.html.en
の値が返ってくるのが分かると思います。ちなみに、デフォルトだと .gz
は application/x-gzip
という MIME タイプに関連付けされているので(TypesConfig
内で)、.html
より右側に .gz
を付けてしまうと、このファイルの MIME タイプが text/html
ではなく、application/x-gzip
と判断されてしまうので注意してください。この enc.gz.html.en
は、「英語で書かれ text/html 形式のファイルで、gzip によりエンコードされている」ファイルなのです。
このエンコーディングを表す拡張子が複数付いていた場合は、どちらのエンコーディングを指定されても、そのファイルは対象となります。
DefaultEncoding
というディレクティブは無いようで、Accept-Encoding:
ヘッダーの条件に合わない場合は、エンコーディング拡張子なしのファイルが使われます。
キャラクタセットとは、一般に文字コードと呼ばれているやつで(正確には文字コードとキャラクタセットは意味が違う、この場合の文字コードという言い方は間違い)、例えば EUC-JP やら、Shift_JIS、ISO-2022-JP がそれに当たります。
つまり、同じ文章でも Shift_JIS や EUC-JP で保存したファイルを用意しておき、どれかしか読めない場合はそのファイルを返すようにするのがこの設定です。ブラウザの Accept-Charset:
ヘッダーを見て判断します。やはり、この値をユーザが設定できるブラウザは少ないようです。Apache の設定は次のようになります。
AddCharset ISO-2022-JP .jis AddCharset Shift_JIS .sjis AddCharset EUC-JP .euc-jp AddCharset UTF-8 .utf-8
この場合も、DefaultCharset
というディレクティブは無いようで、Accept-Charset:
ヘッダーの条件に合わない場合は、エンコーディング拡張子なしのファイルが使われます。
それでは、次のようにしてファイルを用意してみましょう。
echo "日本語 ISO-2022-JP" | iconv -f EUC-JP -t ISO-2022-JP > char.jis.html.ja echo "日本語 Shift_JIS" | iconv -f EUC-JP -t SHIFT_JIS > char.sjis.html.ja echo "日本語 EUC" > char.euc-jp.html.ja echo "日本語 UTF-8" | iconv -f EUC-JP -t UTF-8 > char.utf-8.html.ja echo "Japanese Unknown" > char.html.ja
この例は端末が EUC-JP の場合です、Shift_JIS の場合などは、-f SHIFT_JIS
などとしてください。iconv
が無い場合は、nkf
(UTF-8 は作れないか)や、jvim, emacs
エディタなどで作ってみてください。
Mozilla 1.0(Windows 版)の場合、次のようなヘッダーを送っています。
Accept-Charset: Shift_JIS, utf-8;q=0.66, *;q=0.66
このことから、Shift_JIS > その他(UTF-8 と * が同じ優先度)という優先度だと分かります。テスト結果はうまくいったでしょうか?
キャラクタセットを表す拡張子が複数付いていた場合は、右側(後ろ側)の拡張子が優先されます。
4種類あるのを説明しましたが、現実的に言うと、普通に使って問題ないのは言語のみで、ファイルタイプはかなり限定されますね。エンコーディングは微妙です。キャラクタセットはあまり必要を感じません(普通のブラウザなら、どのキャラクタセットでも問題ない気がします)。
エンコーディングは IE の場合 Accept-Encoding:
ヘッダーを送りませんが、gzip 形式で送ってもきちんと解釈できます。gzip 形式にすると、転送するデータ量が減るため、ネットワークの帯域を有効利用できる、レスポンスが速くなる、というメリットはありますね。だから対応したブラウザのために gzip 形式も用意しておくと得するかもしれません(転送料によって課金されるタイプの ISP などを使っている人は)。
言語はきちんと用意さえすれば、ユーザにあった言語を表示できる仕組みなので便利だとは思います。例えば、DefaultLanguage en
としておいて、サポートしたい言語を、index.html.ja, index.html.fr
などと作っておけば、/index.html
に対して適切な言語のファイルを返すでしょう。この場合、一見言語の切り替えをしているようには見えないので、非常にスマートだと思います。
ただし、日本人で英語版のブラウザを使っていたりすると、Accept-Language:
ヘッダーで en
しか送ってこなかったりもするので、言語を切り替える説明のページとかが必要だと思います。