## 要約
- S3に保存した画像URLに対してCloudFront経由でブラウザからアクセスする処理が2種類ある
- 1つはHTMLのimgタグで呼び出す場合
- もう1つはJavaScriptの
new Image()
などでImageElementを操作する場合
- S3とCloudFrontの両方で
Access-Control-Allow-Origin
Headerを正しく設定しているのにJavaScript上で操作する時にCORSエラーが発生したりしなかったりする現象が発生 - S3の画像URLを使用している場合、かつ、Chromeでブラウザのcacheが有効になっている場合、2度目のアクセス時に
Access-Control-Allow-Origin
Headerの付いていないキャッシュしたレスポンスをChromeが返すためCORSエラーが発生してしまう
問題が発生してたコード
- 次のように同じ画像URLに対してHTMLのimageタグで使用、かつJavaScript上でも同じ画像に対して処理を行おうとしていた
<img src="https://hoge.cloudfront.net/fuga.jpeg">
const image = document.createElement('img');
image.src = "https://hoge.cloudfront.net/fuga.jpeg"
しかし、JavaScript上のコードで次のようなCORSエラーが発生したりしなかったりする現象が発生した
Access to image at 'https://hoge.cloudfront.net/fuga.jpeg' from origin 'https://hoge.cloudfront.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
S3やCloudFrontの設定の問題?
- 調べていたところ、S3やCloudFrontで
Access-Control-Allow-Origin
Headerが設定されていない場合のケースが出てきたのですが、既に設定済みだった- そもそもHTML上では問題なく使えているので、JavaScript上でだけ発生する理由が説明できない・・・
- ちなみに未設定の場合は次のように設定できる
-
document.getElementById()
でHTMLの画像タグを取得してJavaScript上で使うようにしたら発生頻度は下がったようだったが、それでも人によっては発生したり発生し続けていて原因が突き止めずにいた
そしてようやく原因を突き止める
- 後日調べ直してみて次の記事を発見
- Chrome S3 Cloudfront: No 'Access-Control-Allow-Origin' header on initial XHR request
- S3の画像URLを使用している場合、かつ、Chromeでブラウザのcacheが有効になっている場合、2度目のアクセス時に
Access-Control-Allow-Origin
Headerの付いていないキャッシュしたレスポンスをChromeが返すためCORSエラーが発生してしまう模様- ChromeのDeveloper ToolでNetworkの
Disabled cache
がONの場合は発生しなかった
- ChromeのDeveloper ToolでNetworkの
- crbugのissueはこれ?
解決策という名の応急処理
- ブラウザ側の挙動の問題なので対応するのがなかなか難しい・・・
- 上記の記事ではAWS LambdaでHeaderを変更する関数を間に挟むようにして対処していたようだが、そのためだけのLamdaを用意するのは手間がかかるので別の方法を検討
- HTMLとJavaScriptで使用する画像URLが同じだとキャッシュされてしまうので、URLの末尾にランダムなクエリパラメーターを追加して使用すれば毎回別のURLになりキャッシュを利用されないのではと考えた
- 今回の場合はバックエンドサーバーからS3の署名付きURLを発行して使用しており、そこにオリジナルのクエリパラメーターを追加すると400エラーになってしまった
- S3の署名付きURLにはExire(有効期限)パラメーターがついており、1秒ごとに更新されるので、HTMLで使用される画像URLとは別にJavaScript上で使用する画像URLを1秒ずらして取得するようにして対応した
- 他に良い対応方法があれば教えてください