- Published on
CORS徹底解説
- Authors
- Name
- あやき
- @ayaki2325
はじめに
対象読者は、CORSを知らないバックエンドエンジニアです。
この記事のゴールは、CORSエラーを自力で解決できるようになることです。
Table of Contents
同一オリジンポリシーとは
まず前提として「同一オリジンポリシー(以降SOPと記載)」を理解しておく必要があります。
SOPはあるオリジンから読み込まれた文書やスクリプトは他のオリジンにあるリソースにアクセスできない、というブラウザのセキュリティルールです。
たとえば、https://my-site.invalid
で読み込まれたスクリプトからhttps://my-site.invalid/api/articles
へアクセスできますが、https://api.my-site.invalid/articles
へはアクセスできません。
オリジンは、スキーム(httpやhttps)、ホスト(ドメイン名)、ポート番号の組み合わせで定義されます。https://my-site.invalid/api/articles
のオリジンはhttps://my-site.invalid
1です。
オリジンが完全一致した場合に同一オリジンとみなされます。
先ほどの例だと、https://my-site.invalid
とhttps://api.my-site.invalid
ではオリジンが異なるため、アクセスできないということです。
CORSとは
CORSはCross-Origin Resource Sharingの略で、異なるオリジン間でリソースを共有するための仕組みです。
要するにSOPの制限を回避するわけです。
そのために、特定のHTTPヘッダーを用いてサーバーがどのオリジンからのリクエストを許可するかを明示します。
SOP自体はセキュリティ上重要ではあるのですが、現実問題として異なるオリジン間で通信したいケースは一般的です。
たとえば、フロントエンドhttps://my-site.invalid
がAPIサーバーhttps://api.my-site.invalid
にアクセスするといったケースはよくあります。
CORSはそのようなケースで、セキュリティを保ちながらリソースを共有するための仕組みとして利用されます。
CORSの仕組み
CORSの契機は異なるオリジンからのHTTPリクエストです。
このリクエストはシンプルリクエストとプリフライトリクエストに分類されます。
前者は文字通りシンプルで、後者はやや複雑です。
シンプルリクエスト
シンプルリクエストは、以下の条件を満たすリクエストです。
- HTTPメソッドが
GET
、POST
、HEAD
のいずれかである - HTTPヘッダーがCORSセーフリクエストヘッダーのみである
Accept
Accept-Language
Content-Language
Content-Type
2Range
- 他にも細かい条件がある。詳細はMDN > オリジン間リソース共有 (CORS) > 単純リクエスト参照。
このシンプルリクエストに対して、サーバー側で何も準備をしていないとCORSエラーが発生します。
このリクエストを許可するには、サーバー側でAccess-Control-Allow-Origin
ヘッダーに許可したいオリジンを指定してレスポンスを返す必要があります。 たとえば、https://my-site.invalid
からのリクエストを許可する場合、以下のようにヘッダーを設定します。
Access-Control-Allow-Origin: https://my-site.invalid
これでCORSエラーが発生せず、https://my-site.invalid
は無事にhttps://api.my-site.invalid
と通信できるようになります。
このヘッダーの値に*
を指定すると、すべてのオリジンからのリクエストを許可します。
しかし、一般公開されたAPIなど*
にしないといけない理由がない限り、セキュリティ上の理由から特定のオリジンを明示することが推奨されます。
プリフライトリクエスト
シンプルリクエストの条件を満たせない場合、ブラウザはプリフライトリクエストを送信します。
シンプルリクエストと異なり、リクエストの許可を得るためだけのやり取りをします。
- ブラウザはHTTPメソッド
OPTIONS
を使用してプリフライトリクエストを送信する。このとき、以下のようにどんなリクエストを許可してほしいかを示すヘッダーを含める。Access-Control-Request-Method
: 使いたいHTTPメソッド(例:DELETE
)Access-Control-Request-Headers
: 使いたいHTTPリクエストヘッダー(例:Content-Type
)
- サーバーはアクセスを許可するオリジンやHTTPメソッド、HTTPリクエストヘッダーを指定してレスポンスを返す。
Access-Control-Allow-Origin
: 許可するオリジン(例:https://my-site.invalid
)Access-Control-Allow-Methods
: 許可するHTTPメソッド(例:GET, POST, DELETE
)Access-Control-Allow-Headers
: 許可するHTTPリクエストヘッダー(例:Content-Type
)
- ブラウザはサーバーからのレスポンスを見て、リクエストを続行するかどうかを判断する。たとえばHTTPメソッド
DELETE
を使いたいのに許可されていない場合はCORSエラーを発生させる。
シンプルリクエストにせよプリフライトリクエストにせよ、サーバー側で適切な設定を行えば、不要なCORSエラーを発生させないで済みます。
ここまでの説明で「設定が面倒くさそうだなー」と思った方、安心してください。
リクエストはブラウザが、レスポンスはWebアプリケーションフレームワークがある程度よしなにやってくれます。
今回紹介した仕組み部分をきちんと把握しておけば、CORSエラーが発生したとしてもその修正自体はそこまで難しくありません。
トラブルシューティング
ここまでの知識があれば、CORSエラーが発生したときにどのように対処すればよいかがわかるはずです。
実際にそうか、よくあるエラーメッセージの要約をいくつか紹介します。
Access-Control-Allow-Origin
が含まれていない: 許可したいオリジンを同ヘッダーに指定する。- プリフライトレスポンスによるとPUTが許可されていない:
Access-Control-Allow-Methods
にPUTを追加する。 Authorization
ヘッダーをリクエストに含める許可がサーバーから得られていない:Access-Control-Allow-Headers
に同ヘッダーを追加する。
CORSのことを知らないと意味不明だと思いますが、CORSの仕組みを理解していれば、エラーメッセージから何をすればよいかがわかるはずです。
まとめ
- 同一オリジンポリシーとは、あるオリジンから読み込まれた文書やスクリプトは他のオリジンにあるリソースにアクセスできない、というブラウザのセキュリティルールのこと
- CORSとは、異なるオリジン間でリソースを共有するための仕組み
- シンプルリクエストとプリフライトリクエストお理解が鍵
- CORSエラーが発生したときは、エラーメッセージをもとにサーバー側の設定を見直す