チャプター確認モーダルの画像をfetchで取得するとCORSエラーになる
Description
https://stg-ultemist-assets.instansys.co.jp/main-quest-backgrounds/prologue-normal.webp
この画像をハードリロードしてfetchで取得するとCORSでエラーになる問題
具体的にはメインクエストのチャプターモーダルで取得する時。
キャッシュが効いてるとエラーにならない。
で、バトルの画像もCDNからfetchで取得すているはずなのになぜCORSエラーにならないのか。違いを調査する
Comments (6)
CDN側でCORS対応はされてた。 が、同じ素材を先にimgで取得してしまうと、originを言わないためACA0(Access-Control-Allow-Origin)が押されない状態でキャッシュされ、その直後にfetchするとCORSエラーになる。
しかし一度取得したあとにリロードするとsw(service worker)が仕事をするためfetchでもエラーが出なくなる。
回避方法としては
- CDN側でAccess-Control-Allow-Origin: * を無条件で常に付与する
- fetch使わずにimgで取得する
- MainQuest.tsxのnew Image()を消す(これが先に走った後にfetchするという一連の流れが問題ではある)
などの方法がある。
CDNのアセットの暗号化/復号化をやるのかどうかで対応が変わる
- 暗号化を「やらない/当面しない」なら → img/CSSに統一(CORSも消える・シンプル)
- 暗号化を「やる」なら → 逆に fetch/dataURL に統一すべき(693行のCSS url()も dataURL に直す)。そして CORS問題は SW かCDN設定(ACAO: *)で面倒を見る
現状 img で取得している箇所が390箇所などあるらしいので、暗号化はあんまり現実的じゃないとおもわれ。確認中。 やるんだったらswに復号を集中させればいけそう?ただハードリロード時、初回ロードなどだとswが効かないからダメかも。またモバイルでいけるのかも不明。
┌─────────────────────────────────────────────┬────────────────────────────────────────┐
│ 使われ方 │ 箇所数 │
├─────────────────────────────────────────────┼────────────────────────────────────────┤
│ getCdnUrl 総参照 │ 約680 │
├─────────────────────────────────────────────┼────────────────────────────────────────┤
│ img / CSS背景 など生URLで表示 │ 約390(img系313+CSS75+Assets.load6) │
├─────────────────────────────────────────────┼────────────────────────────────────────┤
│ 既に復号ルート(useImageCache/loadAsset)経由 │ 約94 │
└─────────────────────────────────────────────┴────────────────────────────────────────┘
本番環境では暗号化/復号化を行う方針らしい。 今後、本番環境に向けた復号化処理の統一化は別課題とする。
現状のCDN側のCORS対応だとこうなる
┌───────────────────────┬─────────┬───────────┐
│ リクエスト │ ACAO │ Vary │
├───────────────────────┼─────────┼───────────┤
│ Originなし(img/CSS) │ ❌ なし │ ❌ なし │
├───────────────────────┼─────────┼───────────┤
│ Originあり(fetch) │ ✅ * │ ✅ Origin │
└───────────────────────┴─────────┴───────────┘
ポイントは「Originなしの応答には Vary すら付かない」こと。 Vary が無いと、ブラウザはそれを URLだけをキーにキャッシュ
する=あらゆるリクエストにマッチしてしまう。だから new Image(Originなし・Vary無し・ACAO無し)がキャッシュに入ると、直後のfetch がそれを拾って死ぬ。これが今回の本当の原因。