localhostと本番環境を行き来してAccess-Control-Allow-Originが~と言われたらこれかも?


概要

localhostで開発し、たまに本番環境を表示しようとすると、Access-Control-Allow-Originが異なるためデータ取得ができない旨のエラーがコンソールに表示されることがありました。

Access to fetch at 'https://storage.googleapis.com/example.appspot.com/example.json' 
from origin 'https://example.com' has been blocked by CORS policy:
The 'Access-Control-Allow-Origin' header has a value 'http://localhost:3000' 
that is not equal to the supplied origin.
Have the server send the header with a valid value, or, if an opaque response serves your needs, 
set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

ユーザーから問い合わせも来ないため放置していましたが、放置できない状況になったため、調査してようやく解決しました。
どうして発生するのか?どうやって解決したのかを解説します。

今回はGoogleのCloud Storageの例ですが、AzureやAWSでも同様だと思います。

読者対象

  • CORSを理解している
  • 複数のOriginを許可している

CORSについての簡単な説明はこちら。
https://developer.mozilla.org/ja/docs/Web/HTTP/CORS
https://cloud.google.com/storage/docs/cross-origin?hl=ja

いきなり結論

localhostのレスポンスヘッダーか本体をキャッシュしていることが原因のようです。
Access-Control-Allow-Origin: localhost:3000を受け取るため、localhostなら問題ないですが、本番環境は当然ながらlocalhost:3000ではないことからエラーになっていたということです。
該当する通信を右クリックしてブラウザキャッシュクリアを選択後、本番環境で実行すれば問題は解消されます。

ただし、Googleへのログインやほかのタブも影響受けますので、実行前にはその他すべてのタブのチェックをして安全に実行しましょう。
不都合がないなら、このような操作が不要なシークレットモードすれば問題を回避できます。

Road to resolution

問題発生

localhostで開発し、たまに本番環境を表示しようとすると、Access-Control-Allow-Originが異なるためデータ取得ができない旨のエラーがコンソールに表示されることがありました。

どうやらシークレットモードでは発生しないようだ

localhostの方のChromeタブを終了してもダメでしたが、シークレットモードであれば問題なく、localhostと併存できます。

localhostは確かに許可しているオリジンの一つではあるが…

もしかして、実はoriginは一つしか設定できない罠だろうか?

cors[].origin[]

配列だからここは問題ない。
サーバーがここのホワイトリストから判断するんだから、複数設定することに何ら問題はない。
それでは何が問題でlocalhost:3000を返してくるのか…

ブラウザのキャッシュに以前の失敗したプリフライト リクエストが入るだとぉ!

いろいろ調べたのですが、これといったものを見つけられず、CSのドキュメントを眺めていた時のことでした。

リクエストとレスポンスが表示されない場合は、ブラウザのキャッシュに以前の失敗したプリフライト リクエストが入っている可能性があります。ブラウザのキャッシュをクリアすると、プリフライトのキャッシュもクリアされます。

ブラウザのキャッシュに入っている可能性?
けど全部クリアしたしなぁ…(デベロッパーツールのclear storageでブラウザのキャッシュは消えるという認識誤り
念のため通信を調べてみよう。

時刻もlocalhostのレスポンスヘッダーと異なるし、わからない…
けどキャッシュは削除したよねぇ…
なんとなく右クリックしたらブラウザキャッシュクリアの項目が
実行したら見事に解決:

Windowsの場合、キャッシュクリアすると下記フォルダのキャッシュファイルがごっそり消えるので注意しましょう。
全部消えるんかーい
C:\Users\<your_user_id>\AppData\Local\Google\Chrome\User Data\Default\Cache

いきなり結論で記述したとおりですが、Googleへのログインやほかのタブも影響受けますので、実行前にはその他すべてのタブのチェックをして安全に実行しましょう。

サーバーから複数のAccess-Control-Allow-Originを返せないか?

サーバーが複数返してくれたらありがたいと思って調査。
仕様上、リストではなく単一オリジンと限定しているのためNGの模様。

NOTE: In practice the origin-list-or-null production is more constrained. Rather than allowing a space-separated list of origins, it is either a single origin or the string "null".

Google翻訳先生曰く

注:実際には、origin-list-or-null生成はより制約されます。 スペースで区切られたオリジンのリストを許可するのではなく、単一のオリジンまたは文字列「null」のいずれかです。

まとめ

キャッシュが意図しないおせっかいをするため、Access-Control-Allow-Originではじかれる問題に遭遇したらシークレットモードを使用するのが無難。