前書き
ブラウザはセキュリティのため、デフォルトでは異なるオリジンのリソースにアクセスできません(同一オリジンポリシー)。
リソースにアクセスできるようにするためには、サーバーがHTTPレスポンスヘッダーを通じて、ブラウザにリソースへのアクセスを許可する必要があります。
このアクセス許可がないとEnsure CORS requesting origin matches resource's allowed origin
のエラーがブラウザのコンソールのログに出力されます。
サーバーからのレスポンスデータが受け取れないだけで、サーバー自体は成功(200)のステータスを返していると思います。(サーバー側でCORSに関してエラーを返す場合はこの限りではありません)
Cloud Run functionsを利用する場合、基本的に異なるオリジンのAPIにアクセスすることになるため、サーバー側(Cloud Run functions側)でHTTPレスポンスヘッダーを通じて、ブラウザにリソースへのアクセスを許可する必要があります。
CORSエラーの解決方法
CORSエラーの解決方法は簡単で、先述したようにCloud Run functionsのAPIでレスポンスヘッダーを設定してあげるだけです。
|
|
プリフライトリクエストについては後述
します。
上記コードのようにAccess-Control-Allow-Origin
に*
を設定すると、すべてのオリジンを許可するということになります。
このコードはHTTP CORS を参考にしています。
*を使用してもいいケース
先述したコードでは*
を使用してすべてのオリジンを許可していますが、例えば、天気情報や通貨換算といったパブリックAPIの場合はあらゆるオリジンからのアクセスが見込まれるため*
の方が利便性の観点から適しています。
また開発環境の場合も*
で問題ありません。
*を避けるべきケース
*
を避けるべきケースはクッキーやセッション情報を扱う場合、*
を使用すると安全性が損なわれるため使用は避けるべきです。
また、APIを使用するオリジンが事前に分かっている場合は基本的に使用を避けましょう。
複数のオリジンを設定したい場合
*
を設定せずに許可したいオリジンを設定する場合についてですが、
Access-Control-Allow-Origin
は1つのオリジンしか設定できないため、設定前にオリジンの判定を行う必要があります。
例えば下記のようなコードになります。
|
|
上記コードでは、ALLOWED_ORIGINS
のリストに定義されているオリジンのみリソースのアクセスを許可することになります。
プリフライトリクエストとは?
プリフライトリクエストのイメージ
以下のような会話をイメージしてください:
ブラウザ:
「ねえサーバーさん、このデータちょっと取ってもいい?でもその前に、安全かどうか確認してから実際に取りたいんだけど、どう?」
サーバー:
「OKだよ!君のリクエストを受け付けるよ!具体的には、この方法とこのヘッダーなら許可するよ!」
プリフライトリクエストが必要な理由
ブラウザは、勝手に他のサーバーにアクセスすると危険なので、上記イメージのように「いきなりリクエストを送るのではなく、事前に確認」を行います。
これは、「クロスオリジンリクエスト」という特別な場合にのみ必要です。
プリフライトリクエストの流れ
事前確認リクエスト(OPTIONSリクエスト)を送信
ブラウザが「プリフライトリクエスト」という特別なリクエストをサーバーに送信。
このリクエストには「どんな操作をしていいか?」という質問が含まれています。
例:
|
|
サーバーが許可情報を返す
サーバーが「こういうリクエストなら受け付けるよ」と答えます。
例:
|
|
ブラウザがリクエストを送信
安全確認が取れたら、実際のリクエスト(GETやPOST)を送信します。
プリフライトリクエストが発生する条件
プリフライトリクエストが必要になるのは、リクエストが特別な場合だけです。以下の条件を満たす場合に発生します:
-
HTTPメソッドが特別:
PUT, DELETE, PATCH など(GET, POST, HEAD は対象外ですが後述するカスタムヘッダーを使用する場合や、特別なContent-Typeを指定する場合はプリフライトリクエストが発生します)。 -
カスタムヘッダーを使用:
例えば、Authorization や X-Custom-Header など独自のヘッダーを使用する場合。 -
Content-Type が特別:
application/json など、特定の種類を使う場合。
プリフライトリクエストがない場合との違い
通常のリクエスト(プリフライトなし)
ブラウザは直接リクエストを送る。
サーバーが許可すればレスポンスを返す。
プリフライトリクエストありの場合
事前に「OPTIONS」リクエストを送信して確認。
確認が成功すれば、次に本番のリクエストを送る。
プリフライトリクエストの例
プリフライトリクエスト
|
|
サーバーの応答
|
|
実際のリクエスト
|
|
※Content-Type
がapplication/json
なのでプリフライトリクエストが発生している
プリフライトリクエストが問題になる場合
パフォーマンスの低下
プリフライトリクエストでは、通常のリクエストよりも1回多くサーバーと通信が発生します。これが全体のパフォーマンスを低下させる原因になることがあります。
サーバーの設定ミス
- サーバーが
OPTIONS
リクエストを正しく処理していないと、リクエストが失敗します。
プリフライトリクエストを回避する方法
-
単純なリクエストにする:
GET
やPOST
を使い、特別なヘッダーやContent-Type
を避けます。
-
結果をキャッシュする:
Access-Control-Max-Age
を設定して、プリフライトリクエストを一定期間キャッシュします。
1
Access-Control-Max-Age: 3600
プリフライトリクエストのまとめ
プリフライトリクエストは、「ブラウザがサーバーに安全確認をするための事前チェック」です。
-
流れ:
- ブラウザがサーバーに「このリクエストしていい?」と事前確認。
- サーバーが「OK!」と許可を返す。
- ブラウザが本番リクエストを送信。
-
ポイント:
- 必要な場合とそうでない場合がある。
- パフォーマンスに影響を与えるため、回避できる場合は回避する工夫が必要。
全体のまとめ
CORSは、ブラウザのセキュリティを高めるための仕組みですが、適切に設定しないとエラーやパフォーマンスの低下が発生します。本番環境では特定のオリジンを明示的に許可し、開発環境では*を使うなど、適切な設定を行うことが重要です。また、プリフライトリクエストが不要な設計を心がけることで、パフォーマンスの改善が期待できます。