Torisugariの日記: ウェブのリダイレクトについて (前)
1. リダイレクトの意味と範囲
リダイレクト(redirection)は、あるいは漢字で「転送」とも呼ばれていたりしますが、言葉の問題として捉えるなら、なかなかに複雑な側面があります。ともすれば、redirectionと全く同じ挙動を、文脈によってrefreshやreloadと呼ぶことがあるからです。不毛な議論を避けるために、ここでは「コンテンツ側がURLを勝手に変更すること」としておきます。
余談ですが、例えば、英Wikipediaの記事「URL redirection」では、新しいリソースの場所をリンクで示す、いわゆる「手動リダイレクト」もリダイレクトに含めています。
例:
このページは移動しました。新しい場所へは __ここ__ をクリックしてください。
ですから、(Here病については勘弁してもらうとして)上のような意味で使うときは、厳密に言えば「リダイレクト」ではなく「自動リダイレクト(自動転送)」とすべきだったのかもしれません。英単語の意味は「方向を変える、転進」ですから、「コンテンツ側がURLを勝手に変更すること」は明らかに意味が狭すぎるわけです。しかしながら、多くの人がこの意味で使っていますし、おそらく誤解を招くことはないと思います。
細かい話をしていけば、HTTPより下層での負荷分散なども「ウェブのリダイレクト」と呼び得るでしょうが、それらもここでは割愛します。
2. リダイレクトの種類
リダイレクトをされるユーザー側からみると、どれも同じに思えますが、リダイレクトを行うコンテンツ側としては、様々な手法があります。とりあえず、それらを総論として"全て"把握するところから始めてみましょう。
- HTTPのステータスコード(301, 302, 303, 307) + Locationヘッダ
- HTTPのRefreshヘッダ
- meta要素のrefresh指示文
- JavaScript
- Locationオブジェクト
- DOMイベント系
- HTMLFormElement::submit()
- HTMLElement::click()
- HTMLDocument::createEvent()
- Historyオブジェクト
- Window::open()
- プラグイン
大きく分けるとこの5つです。では、各々を順に見ていきます。
2.1 HTTPの300代ステータスコード(301, 302, 303, 307) と Locationヘッダによるリダイレクト
おそらく、先に挙げた5つのうち、この方法が最も一般的に使われているリダイレクトでしょう。ただし、少なくともLocationヘッダでリダイレクト先を指定しなければいけませんから、.htaccessなどの設定ファイル、もしくはHTTPのResponse Header(応答ヘッダ)を記述できるCGIを作る権限が必要となります。現代的なホスティングサービスでは、その手の権限で困っている人は少ないとは思いますけれど、そういう意味では最もハードルが高い手法でもあります。
具体例や各ステータスコードの詳しい説明は他の解説に譲るとして、細かく見ると、まず、307だけはPOSTを含め全てのメソッドを通す、という点に註意が必要です。逆に、303が通すのはGETとHEADだけです。私は、ウェブ開発者たちは規格と良識に従ってステータスコードを選んでいると信じていますが、実用上、307と303を使い分ける理由は「(掲示板などの)フォームからのリクエストか否か」に尽きるでしょう。自分が作った掲示板からのリクエストなら307でないとまずいでしょうし、あるいは、他人の作ったフォームから踏まれる可能性があるなら、安全性を考慮して303にすべき場合もあるでしょう。
問題は301と302です。POSTメソッドに関して、301(及び302)はPOSTを通すべきだとRFC 2616には書いてあります。
Note: When automatically redirecting a POST request after receiving a 301 status code, some existing HTTP/1.0 user agents will erroneously change it into a GET request.
この"some existing HTTP/1.0 user agents"はNetscapeとIEのことで、実質的に名指しで"erroneously"と非難しているわけですが、Trident系(IE), Gecko系(Firefox), Webkit系(Safari, Chrome)は現在も301でPOSTメソッドを通しません。つまり、"some existing HTTP/1.0 user agents"の後継者である主要なexisting HTTP/1.1 user agentsの全ては、POSTをGETに変換します。Geckoに関してはBug 190630、W3CのメーリングリストはIssue 160を参照してください。XMLHttpRequestとの整合性に関しては、XMLHttpRequest HTTP Feature Testsが参考になると思います。
結局のところ、どのような理由にしろ、GETとHEAD以外を使う場合は307にすべきでしょう。
ステータスコードの300は、ja.wikipedia.orgで挙げられている例の「XHTML1.1のDTD」のように、選択肢を示す必要があります。つまり、典型的な手動リダイレクトの例であって、本来はこの文章の守備範囲外です。しかし、Geckoの場合、301、302、303、307の4つに加えて300もリダイレクトの対象になっており、Locationヘッダを指定しておけばリダイレクトされます。私は決して勧めませんが、興味がある人は他のブラウザの挙動も調べてみるといいでしょう。
最後に、リダイレクトに最も興味がある層はSEO関連だと思いますので、一言付け加えておくことにしますが、ユーザーにとって最も歓迎すべきリダイレクトは301でしょう。そして、検索エンジンにとってもそれは同じですから、301には最大限の配慮がなされているといっても過言ではなく、301を奨励する文言を随所で見かけます。私も大変結構なことだと思います。ただし、Cache-controlなどで指定しない限り、301のResponse Headerはほぼ無限のキャッシュ寿命を持ちます。つまり、一度301リダイレクトにしてしまうと、「やっぱりやめた」というのは利きません。普通のページならユーザーが強制リロードすることもあるかもしれませんが、リダイレクトの場合は、当然、リロードが不可能です。300番台はResponse Bodyのサイズもゼロなので、ユーザーが明示的にキャッシュを消さない限り残り続ける可能性があります。こうなると、サーバー側が新たにどういう設定にしようとも、それをUAが読みに行くことはありません。だからこそ、301は歓迎すべきリダイレクトなのです。しかし、後で別の用途に使う場合、取り返しがつきませんから、ファイル階層の上の方、例えばトップページなんかを301にする際は、もう二度と使わないという決意を要します。そして、敢えてやるなら、Cache-controlの指定が必須なんだろうと思います。ちなみに、私は実験的にトップページを301にしていますが、キャッシュ云々の制御はしていません。
2.2 HTTPのRefreshヘッダによるリダイレクト
HTTPの仕様にはRefreshヘッダに関する取り決めがありません。つまり、独自仕様なのですが、主要なブラウザは全て対応しています。Refreshはもともと、サーバーからのプッシュを擬似的に実現するために定期的なリロードを促す機能ですから、これをリダイレクトに使うことはまずないでしょう。ただ、メリットはなくとも、可能ではあります。
ウェブのリダイレクトについて (前) More ログイン