Cross Origin Resource Sharingのプリフライトの背景を理解する

Ajaxで他のホストのデータにアクセスできるようになるやつについてです。

この辺のドキュメントを読んでいても、どうも今ひとつ納得できなかったりすることがないかと思います。

  • シンプルなリクエストとプリフライトリクエストはなにが違うの?
  • まずシンプルなリクエストとプリフライトリクエストをなぜ区別しているの?
  • プリフライトリクエストは「実際のリクエストを送信しても安全かを確かめるために」あると書いてあるが、「安全な」リクエストと「安全でない」リクエストはどう違うの?

シンプルなリクエストとプリフライトリクエストの二つがあることはわかるけど、なぜ二つに分けないといけないのか良くわからないし、その定義もなんか無駄に複雑に見えて良く理解できない。なぜ application/x-www-form-urlencodedが特別扱いされているのか。そもそも、プリフライトリクエストとはなんのためのものなのかは、全然具体的に説明されていない。プリフライトリクエストではなにをすれば良いのだろう。何も考えずに200を返すだけで良いのだろうか。何らかの認証を行うのは、プリフライトリクエストのなかでやって良いのだろうか?キャッシュの時間はどの程度の長さにして良いのだろう?

まあ、こんな感じ。


さて、適当にぐぐってみると、Stack Overflowの記事が見つかります。

おお。これっぽい。

プリフライトリクエストの背景

簡単に言うと、

  • シンプルなリクエストは、元々Webブラウザで発行可能だったクロスドメインなリクエスト
    • これまでもできていたことなので、これからもできないといけない
    • Webアプリケーションの責任として、このようなリクエストはきちんと処理できなくてはいけない
  • プリフライトリクエストは、CORSによって発行可能になったリクエスト
    • この種類のリクエストが発行されたときにきちんと動作しないのは、Webアプリケーションの責任ではない
    • Webブラウザの側で、リクエストを発行して大丈夫であることを確認しないといけない

ということです。

シンプルなリクエストを良く見てみると、

  • カスタムヘッダなしのGETはこれまでもWebブラウザから発行できていたリクエストなので、特別な対応は必要ない → シンプルなリクエスト
  • 普通のformを通して他のドメインにPOSTできていたようなもの(カスタムヘッダがないとか、content-typeがapplication/x-www-form-urlencodedとか)は、これまでも普通にWebブラウザから発行できていたので、普通のWebアプリケーションは処理できるはず → シンプルなリクエスト

ということです。

一方でプリフライトリクエストはと言うと、それ以外のもので、つまりXHRとかを使わないとブラウザ内からは発行できないものです。CORSで初めて発行可能になったリクエストなので、Webサーバが対応しているかどうかを、確実に検査する必要があるわけですね。

サーバがCORSに対応しているかどうかって……Access-Control-Allow-Originと何が違うの?

一瞬混乱するかもしれませんが、違います。

Access-Control-Allow-Originは、リクエストが処理されて、ブラウザに返ってきたときに見えるやつなので、POSTみたいな冪等でないリクエストの場合にはやくにたちません。ブラウザに返事が返ってきた時点では、すでにPOSTが処理されてしまっていて、サーバのデータが変更されてしまっているわけです。

まとめ

  • プリフライトリクエストはWebアプリケーションがCORSに対応しているかどうかを確認するためのもの
  • 対応してるなら、深く考えずに200返しちゃって問題ない(多分)
  • キャッシュの時間も「やっぱりCORS止めます!」みたいなことがないなら、適当に決めれば良い(多分)