ブラウザのPOSTリクエストは、リダイレクトさせるとGETに化ける?
Posted: Updated:
POSTがGETになってる?
前回のエントリーにもつながっている話ですが、ブラウザからフォームでPOSTされたものがリダイレクトされたときの挙動について。挙動が理解できると、化けるってのも失礼な表現なんですが。以下、化けた理由と、HTTP周りの実装の話をつらつらと。
普通のHTMLなフォームからPOST
<form action="post_receiver" method="post" enctype="multipart/form-data"> <input type="file" name="imageFile" /> <input type="submit" value="画像をアップロードしてやんよ" /> </form>
上記のようなHTMLなフォームから、POSTした際にサーバー側でうまくPOSTデータを受け取れないことがありました。
echo $_SERVER['REQUEST_METHOD']; // GET
なんでだろー?と思って、PHP側で、$_SERVER変数をダンプしてみたら、なぜかリクエストがGETに変質していました。
Live HTTP Headersでリクエストとレスポンスを追跡
お馴染みのLive HTTP Headers :: Add-ons for Firefoxで、リクエストとレスポンスを追跡してみました。
まずはフォームからPOSTすると...
普通にPOSTでリクエストが飛んでいます。
http://example.com/post_receiver POST /post_receiver HTTP/1.1 Host: example.com User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json Accept-Language: ja,en-us;q=0.7,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 115 Connection: keep-alive Content-Type: multipart/form-data; boundary=---------------------------184868242010120281441086531968 Content-Length: 231 -----------------------------184868242010120281441086531968 Content-Disposition: form-data; name="imageFile"; filename="" Content-Type: application/octet-stream -----------------------------184868242010120281441086531968--
301リダイレクトされた
リクエストされたパスの末尾にスラッシュを加えて、URLを正規化するときのリダイレクト。mod_rewriteの動作ですね。
HTTP/1.1 301 Moved Permanently Date: Tue, 09 Nov 2010 07:12:42 GMT Server: Apache Location: http://example.com/post_receiver/ Vary: Accept-Encoding Content-Encoding: gzip Content-Length: 197 Connection: close Content-Type: text/html; charset=iso-8859-1
素直にリダイレクトするわけですが
ここでリクエストがGETになっています。POSTデータも消滅。ぎゃー。
http://example.com/post_receiver/ GET /post_receiver/ HTTP/1.1 Host: example.com User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json Accept-Language: ja,en-us;q=0.7,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 115 Connection: keep-alive Referer: http://example.com/ HTTP/1.1 200 OK Date: Tue, 09 Nov 2010 07:31:52 GMT Server: Apache X-Powered-By: PHP/5.2.14 Vary: Accept-Encoding,User-Agent Content-Encoding: gzip Content-Length: 22 Connection: close Content-Type: text/html; charset=UTF-8
301, 302, 303, 307のステータスコードが返ると、ブラウザはGETでリダイレクトする
つまり、そういうことらしいです。確かに、ユーザーからすれば何処にリダイレクトされたかも分からないようなリソースに、POSTデータを勝手に送ってしまっては少々お行儀が悪いですね。
上の参考エントリー内でも言及されていますが、HTTPステータスコードの詳細を見てみると、確認さえすれば元のメソッドでリダイレクトすべきものもあります。何でもかんでも暗黙裏にGETメソッドでリダイレクトさせてしまう現実のブラウザは、本来の仕様とは異なった実装がされているということになります。
リダイレクト系のステータスコードについて
- 301 Moved Permanently
- 恒久的な現在のリソースのURIを示す。
- 302 Found ( Moved Temporarily )
- 一時的な現在のリソースのURIを示す。GET・HEAD以外のリクエストのレスポンスとして受信した際、ユーザーの確認なしにリクエストをリダイレクトしてはならないし、リクエストメソッドを変更してもならない。
- 303 See Other
- リクエストの後に、GETで回収すべきリソースのURIを示す。update.phpにPOSTした後に、content.htmlをGETして欲しい時などに使う。
- 307 Temporary Redirect
- 302と同様の意味をもつ。リダイレクトのポリシーも同じ。
元々、301と302があったわけですが、昔から302が本来の意図を上回って色々な使われ方をしてきました。一部の使い方は、不適切な使い方とされ、302の本来の意味がぼやけてしまいました。
そこで、HTTP1.1で新しいステータスコードが作られ、302で解決すべきでなかった用途を新たに満たす「303」と、302の本来の意味を明示的に分離させた「307」が追加されましたという経緯らしいです。
ステータスコードを新設したところで、これまでに積み上げられてきたシステムは、ブラウザの302周りの誤った実装に依存しているものも多いでしょうから、ブラウザ側もそう簡単には実装を変えられない、って事情でしょうか。それならそれで、せめて307は仕様通りの実装にすべきだと思いますが。
POST先でのリダイレクトには気を遣おう!
基本的にPOSTリクエストをリダイレクトで、どこかに受け流すことはできないし、mod_rewriteでうっかりリダイレクトされる時も気をつけたい。今回の自分みたく、末尾のスラッシュをつけるためのリダイレクトの時とか...。正しい実装という観点からすれば、303や307もちゃんと使い分けて送出するようにしたいですね。
ではでは。