OPTIONSメソッドが気になったのでCORSとプリフライトリクエストについて少し調べました
Amazon API GatewayでCORSを有効にするとOPTIONSメソッドが追加されるのが気になったので、CORSについて少し調べました。
CORSとは
オリジン間リソース共有Cross-Origin Resource Sharing (CORS) は、追加の HTTP ヘッダーを使用して、あるオリジン (ドメイン) で動作しているウェブアプリケーションに、異なるオリジンのサーバーにある選択されたリソースへのアクセスを許可することができる仕組みです。ウェブアプリケーションは、自分のオリジンとは異なるオリジン (ドメイン、プロトコル、ポート番号) からリソースをリクエストするとき、オリジン間 HTTP リクエストを発行します。*1
クライアントサイドの実装は、最近ではIEのごく一部のブラウザ以外で実装されていて、JavaScriptで異なるドメインのサーバへアクセスしようとした時によくエラーになっているあれです。
CORSが必要な理由
CORSのことは一旦置いておいて、Webサーバにリクエストを送った時のことを考えましょう。基本的に、Webサーバはリクエストを送ってきたクライアントのIPにレスポンスを返します。
たとえば自社でAPIとWebページを開発しているときは以下のような感じになりますね。
ここでもし、自社API(図中の api.example.com
)に他者のホームページからリクエストが
のように送信されていたらどうなるでしょうか。不正なリクエストを送ってサーバに不具合を起こされたり、顧客に本物のWebサイトと偽って表示して顧客に表示するためのコンテンツを盗まれたり、といったことは起こるかもしれません。
CORSはこのような場合に役に立つもので、CORSを用いると、Webサイトのものとは異なるドメインにリクエストを送る場合、そのリクエストが許可されていることをサーバに一度確認してからでないとPOSTなどのHTTPリクエストが送ることができなくなります*2。
CORSの仕組み
CORSを用いると、Webサイトのものとは異なるドメインにリクエストを送る場合、そのリクエストが許可されていることをサーバに一度確認してからでないとPOSTなどのHTTPリクエストが送ることができなくなります
と書きましたが、具体的にどのようにやっているのか、基本的なパターンについてさらっておきます*3。
プリフライトリクエスト
Content-Type
が text/plain
でそれ以外にめぼしいリクエストヘッダを含まないGETリクエストなど一部の単純なもの以外*4、
Webページ上のJavaScriptが外部ドメインにリクエストを送ろうとした際、Webブラウザは元のHTTPリクエストを送るのではなく、 プリフライトリクエスト
というものを送信します。
この プリフライトリクエスト
には
が含まれ、この プリフライトリクエスト
を受け取ったサーバは、
- サーバがリクエストを許可する外部サーバのドメイン一覧である
Access-Control-Allow-Origin
- サーバがリクエストを許可するリクエストメソッドの一覧である
Access-Control-Allow-Methods
- サーバがリクエストを許可するリクエストヘッダの一覧である
Access-Control-Allow-Headers
- プリフライトリクエストをしばらく有効にしておく期限を表す
Access-Control-Max-Age
が含まれるレスポンスを返します。Webブラウザはこのレスポンスを見て、自分が送ろうとしていたリクエストが外部サーバから許可されているかを確認し、許可されているのを確認できたら元々送信したかったリクエストを送信します。
サーバから通信を許可されていないドメインであったり、許可されていないメソッドでリクエストしようとしている場合は、Webブラウザはリクエストの送信を断念します。
参考
- 全般的に
- https://developer.mozilla.org/ja/docs/Web/HTTP/CORS
- https://dev.classmethod.jp/etc/about-cors/ 具体的なリクエストとレスポンスの記載がありがたかった
- 実装について
- https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34 クッキーを許可する場合やシンプルなデータ読み込みなどの実装
- サーバ側での実装時に苦労したことについて https://devpixiv.hatenablog.com/entry/2014/12/16/181804
*1:https://developer.mozilla.org/ja/docs/Web/HTTP/CORS
*2:GETのみなど一部確認用のリクエスト(プリフライトリクエスト)なしに送ることも可能です(単純なリクエスト)。
*3:リクエストそのものだけでなくクッキーを扱えるかどうかなどもCORSを用いて制御することができます https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34#cookie%E3%82%82%E8%A8%B1%E5%8F%AF%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88
*4:詳しくは https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#Simple_requests