Access-Control-Allow-Origin 完全ガイド:仕組み・設定・セキュリティと実践例

概要

Access-Control-Allow-Origin は、ブラウザの同一生成元ポリシー(Same-Origin Policy)によって制限されたクロスオリジンのリソースアクセスを制御するための HTTP レスポンスヘッダです。サーバがこのヘッダで許可したオリジン(もしくはワイルドカード "*")に対して、ブラウザは JavaScript などからレスポンスの内容を読み取ることができます。本コラムでは仕組み、関連ヘッダ、実装例、注意点やベストプラクティスまで詳しく解説します。

同一生成元ポリシー(SOP)との関係

同一生成元ポリシーは、プロトコル(scheme)、ホスト(host)、ポート(port)の三要素が同じでない場合、あるドキュメントが別のオリジンから取得したリソースのコンテンツを読み取ることを原則禁止します。例:https://example.com と https://api.example.com は別オリジンです。

CORS(Cross-Origin Resource Sharing)は、サーバが特定の条件下で他オリジンからのアクセスを明示的に許可する仕組みで、Access-Control-Allow-Origin はその中核にあるヘッダです。

Access-Control-Allow-Origin の動作と値の意味

  • 単一オリジン(例: Access-Control-Allow-Origin: https://example.com): 指定したオリジンのみを許可します。
  • ワイルドカード(*): すべてのオリジンを許可します。ただし、クレデンシャル(クッキーや認証ヘッダ)を伴うリクエストでは使用できません。
  • 複数オリジンの直接列挙は不可: Access-Control-Allow-Origin: https://a.com, https://b.com という形式は仕様上無効です。複数オリジンを許可したい場合は、サーバ側でリクエストの Origin を確認し、条件に応じてレスポンスにそのオリジンをそのまま返す実装が必要です。
  • Origin: null(file://、data:、sandboxed iframe などのオリジン不定の場合)の扱いはブラウザ依存の部分があり、セキュリティ上の理由で慎重に扱う必要があります。

プリフライトと関連ヘッダ

単純リクエスト(GET/POST の一部、特定の Content-Type など)ではプリフライトが不要ですが、カスタムヘッダや複雑なメソッド(PUT/DELETE 等)を使う場合はブラウザが事前に OPTIONS リクエスト(プリフライト)を送ります。プリフライトに対するレスポンスでよく使われるヘッダ:

  • Access-Control-Allow-Methods: 許可する HTTP メソッド(例: GET, POST, OPTIONS
  • Access-Control-Allow-Headers: リクエストで使用を許可するカスタムヘッダ
  • Access-Control-Max-Age: プリフライト結果をブラウザがキャッシュする秒数
  • Access-Control-Allow-Credentials: クレデンシャルを含むリクエストを許可するか(true または未指定)
  • Access-Control-Expose-Headers: ブラウザの JavaScript からアクセス可能にするレスポンスヘッダ名

重要な実装ルールと注意点

  • クレデンシャル付きリクエストでは、Access-Control-Allow-Origin* を指定できません。代わりにリクエストの Origin をそのまま返して、かつ Access-Control-Allow-Credentials: true を返す必要があります。
  • パフォーマンスと正確なキャッシュのために、レスポンスヘッダに Vary: Origin を付けることが推奨されます。これを付けないと CDN やプロキシが誤って異なるオリジン向けレスポンスを使い回す恐れがあります。
  • ブラウザは CORS ポリシーを実施しますが、curl やサーバ間通信は CORS を強制しません。curl で動く=ブラウザで動く、ではない点に注意してください。ブラウザの挙動を確認するには実際にブラウザか、Origin ヘッダを付与したリクエストで検証します(例: curl -H "Origin: https://example.com" -I https://api.example.com)。
  • Access-Control-Allow-Origin はレスポンスの読み取り可否を制御するものであり、サーバ側で不正なアクセスを防ぐ認可メカニズムの代替にはなりません。サーバは独立して認証・認可を実施する必要があります。

実践例(サーバ側設定)

Node.js(Express)の簡単な例: リクエストの Origin をチェックして返すパターン。

const allowed = ['https://example.com','https://app.example.com'];
app.use((req,res,next)=>{
  const origin = req.get('Origin');
  if(allowed.includes(origin)){
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Vary', 'Origin');
  }
  if(req.method === 'OPTIONS'){
    res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
    return res.sendStatus(204);
  }
  next();
});

Nginx(リバースプロキシ)例:

location /api/ {
  if ($http_origin ~* (https?://(example\.com|app\.example\.com))) {
    add_header 'Access-Control-Allow-Origin' "$http_origin" always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Vary' 'Origin' always;
  }
  if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
    return 204;
  }
}

よくある誤解とトラブルシューティング

  • 「Access-Control-Allow-Origin を付ければ安全」:CORS はブラウザのクライアント制御であり、サーバ側の認証やアクセス制御の代替ではありません。
  • 「複数オリジンはカンマ区切りで指定できる」:できません。サーバで動的に Origin を反映してください。
  • 「ワイルドカードでクッキーも許可できる」:不可。クッキーを含めたいなら明示的に Origin を返し、かつ Access-Control-Allow-Credentials: true を付けます。
  • 「プリフライトの失敗はサーバエラーではない」:OPTIONS に対して適切な Access-Control-Allow-* を返さないとブラウザは本リクエストをブロックします。サーバ側で OPTIONS を正しく処理すること。

セキュリティ上のベストプラクティス

  • 許可するオリジンを最小化する(ワイルドカードは開発時以外避ける)
  • サーバ側で Origin 値を検証してから反映する(信頼済みリストを使用)
  • 本当に必要なエンドポイントのみクレデンシャルを許可する
  • レスポンスに Vary: Origin を付けてキャッシュの誤動作を防ぐ
  • プリフライトのキャッシュ寿命(Access-Control-Max-Age)を適切に設定して不要なプリフライトを減らす

デバッグのコツ

ブラウザのコンソールでの典型的なメッセージ: "Access to fetch at 'https://api.example.com/data' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource." これはレスポンスに該当ヘッダがないか、値が適合していないことを示します。

curl での確認方法(ブラウザと同様の検証をするには Origin ヘッダを付ける):

curl -i -H "Origin: https://example.com" https://api.example.com/data

curl はレスポンスを返すだけでブラウザのブロック挙動を再現しないため、レスポンスヘッダを目視で確認してください。

まとめ

Access-Control-Allow-Origin はクロスオリジンのレスポンス読み取りを制御する重要なヘッダであり、正しく設定することでセキュリティと利便性のバランスをとれます。ポイントは(1)ワイルドカードの危険性、(2)クレデンシャルとワイルドカードは併用不可、(3)複数オリジンは動的に反映すること、(4)必ず Vary: Origin を考慮すること、です。実運用ではサーバ側で Origin を検証して必要最小限の許可に留め、プリフライトやキャッシュ挙動も含めて設定を行ってください。

参考文献