CDNと静的アセットのCORS設定

CDNからフォント、画像、スクリプトを配信するためのCORSヘッダー設定。S3、CloudFront、Cloudflare、フォント読み込みのよくある問題を解説します。

Common Issues

詳細な説明

CDNアセットにCORSが必要な理由

https://app.example.comのWebページがhttps://cdn.example.comからフォントを読み込む場合、ブラウザはこれをクロスオリジンリクエストとして扱います。CORSヘッダーなしでは、フォントがブロックされ(特にFirefox)、FOUT(Flash of Unstyled Text)や文字化けが発生します。

CORSが必要なリソース

リソース CORSが必要?
フォント(@font-face はい — 常にクロスオリジンブロック
<canvas>内の画像 はいdrawImageでcanvasが汚染される
JavaScriptモジュール はい<script type="module">はCORSを使用
通常の<script> いいえ — "no-cors"モードで読み込み
通常の<img> いいえ — canvas経由で読み取る場合を除く
CSSスタイルシート いいえ — fetch()を使用する場合を除く

AWS S3 + CloudFront

S3バケットCORS設定:

[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "HEAD"],
    "AllowedOrigins": ["*"],
    "ExposeHeaders": ["Content-Length", "Content-Type", "ETag"],
    "MaxAgeSeconds": 86400
  }
]

CloudFront: Origin Request PolicyOriginを追加し、Response Headers PolicyAccess-Control-Allow-Originを追加します。

Cloudflare

Cloudflare WorkersでCORSヘッダーを追加:

addEventListener("fetch", (event) => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const response = await fetch(request);
  const newHeaders = new Headers(response.headers);
  newHeaders.set("Access-Control-Allow-Origin", "*");
  newHeaders.set("Access-Control-Max-Age", "86400");
  return new Response(response.body, { ...response, headers: newHeaders });
}

Nginx静的ファイル向け

location ~* \.(woff2?|ttf|eot|otf|svg|png|jpg|webp)$ {
    add_header Access-Control-Allow-Origin "*" always;
    add_header Access-Control-Max-Age 86400 always;
    add_header Cache-Control "public, max-age=31536000, immutable";
}

フォント固有のヒント

フォントがChromeでは読み込まれるがFirefoxではない場合、ほぼ確実にCORSの問題です。Firefoxは@font-faceリソースのCORS強制がより厳格です。

ユースケース

WebアプリケーションがCDNサブドメインからカスタムWebフォントを読み込んでいます。ChromeではフォントがレンダリングされますがFirefoxでは表示されません。修正はフォントファイルに対するCDNのレスポンスヘッダーにAccess-Control-Allow-Origin: *を追加することです。

試してみる — CORS Header Builder

フルツールを開く