こんにちは。鈴木商店の若林 (@itigoore01) です。
タイトルどおりですが、iframeを使ったら急にCORSで怒られることになる罠にハマったので、同じ状況に困った方に役立てて頂きたく原因と解決策をここに残します。
かなり状況が限定されているので、同じ状況になる人が出てきて、かつこのページを見つけてもらえる可能性はかなり低そうですが……。
同じく状況は限られますが他にもCORSについての記事を書いているので、この記事の状況とは違う場合は以下の記事もご参照ください。
【Angular】「[Error] Blocked 〜 cross-origin request.」のようなエラーが出たときの対処方法
状況
ではまず、エラーとしては以下のようなものです。
1 2 |
Access to XMLHttpRequest at 'https://xxxxxx.xxx/path_to_content' from origin 'https://xxxxxx.xxx' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://xxxxxx.xxx' that is not equal to the supplied origin. |
状況としては以下のような感じです。
- ChromeとSafariで確認(他ブラウザは確認していないだけなので、同様の状況が発生する可能性あり)
iframe
タグを記述している親ページ(以下親ページ
)と、iframe
のsrc
で指定されているページ(以下iframeページ
)は別オリジン- また画像を取得しにいくサーバーと、親ページ、iframeページも別オリジン
- もちろん親ページもiframeページも両方ともサーバー側の
Allow-Origin
設定は済ませている - 親ページとiframeページで同じ画像データをAjaxで取得しており、これがCORSエラーで怒られる
- キャッシュを削除した状態で、最初に画像取得のリクエストを投げたページだけはCORSエラーが発生せずにちゃんと取得できる
- しかし、あとに取得することになったページはCORSエラーが発生してしまう
↑こんな感じです。
キャッシュのくだりで察しのいい方はわかるかも知れませんが、原因を一言でいうとキャッシュです。
では、原因から見てみましょう。
原因
話を単純にするために、はじめにリクエストを投げたページは親ページ、その後リクエストを投げてCORSエラーが発生するページをiframeページとします。
また、親ページはhttps://xxxxxx.xxx
iframeページはhttps://zzzzzz.zzz
とします。
処理としては、まず親ページは画像Aを取得するためにリクエストを投げます。
クロスオリジンリクエストなのでpreflightリクエストのあとGETとなりますが、そのときのレスポンスは以下のように Access-Control-Allow-Origin
がヘッダーに設定されます。
1 2 3 4 5 |
Access-Control-Allow-Methods: HEAD, GET, POST, PUT, PATCH, DELETE Access-Control-Allow-Origin: https://xxxxxx.xxx Content-Length: 9687 Content-Type: image/jpeg |
ここで、ブラウザちゃんはえらいのでレスポンスをキャッシュしてくれます。
次にiframeページで同じく画像Aを取得するためのリクエストを投げます。
するとブラウザちゃんは、良かれと思ってキャッシュしていたレスポンスを返してくれます、ヘッダーもそのまま。
1 2 3 4 5 |
Access-Control-Allow-Methods: HEAD, GET, POST, PUT, PATCH, DELETE Access-Control-Allow-Origin: https://xxxxxx.xxx Content-Length: 9687 Content-Type: image/jpeg |
iframeページは https://zzzzzz.zzz
、しかしヘッダーの Access-Control-Allow-Origin
はhttps://xxxxxx.xxx
。
Access-Control-Allow-Origin
にはhttps://zzzzzz.zzz
は設定されていないため、CORSエラーが発生しているよ!とブラウザちゃんは怒る。
これが原因です。
解決策
結局キャッシュが原因なので、いわゆるCache Bustingが使えます。
例えば親ページのほうだけ https://aaaaaa.aaa/image.jpg?v=${ランダムななにか}
のように Cache Bustingするためのクエリ文字列を付与してあげれば、親ページとiframeページで同じURLの画像を見に行くことがなくなるので、しっかり取得できるようになります。