CORSエラー(Access-Control-Allow-Origin が無い)

「has been blocked by CORS policy」で API 呼び出しが失敗する原因を、プリフライトや credentials の罠まで整理。なぜフロントではなくサーバー側で直すのかまで腹落ちして即解決できる。

応用CORSWebHTTPブラウザセキュリティAPI最終更新: 2026-07-28
3つの要点
TL;DR
  1. CORS はブラウザが実施するクロスオリジン読み取りの安全機構で、サーバーが Access-Control-Allow-Origin 応答ヘッダで明示的に許可しない限り、レスポンスの中身を JavaScript に渡さずブロックする。
  2. 主因は許可ヘッダ不足、OPTIONSが2xxを返さない、credentialsとワイルドカードを併用する3つ。ブラウザのNetworkでOPTIONSと応答ヘッダを確認する。
  3. 直すのは応答を返すサーバー側。ブラウザは正しいオリジンにだけ許可を出すサーバーを信頼して安全を保つため、フロント側では回避できない。開発時のみプロキシで逃がす。

一言でいうと(このエラーが何を意味するか)

Access to fetch at 'https://api.example.com' from origin 'https://app.example.com' has been blocked by CORS policy は、サーバーの応答は正常に届いているのに、ブラウザがその中身を JavaScript に渡すのを拒否した状態です。ネットワーク障害でも 404 でもありません。

前提となるのが同一オリジンポリシー(Same-Origin Policy)です。オリジンとは「スキーム・ホスト・ポート」の3つ組で、https://app.example.comhttps://api.example.com はホストが違うので別オリジンです。ブラウザは、あるオリジンのページが別オリジンの応答を fetchXMLHttpRequest読み取ることを既定で禁止します。これがなければ、悪意あるサイトがあなたのログイン済みクッキーを使って銀行 API を叩き、残高を読み取れてしまうためです。

CORS(Cross-Origin Resource Sharing)は、この禁止をサーバー側から選択的に解除する仕組みです。サーバーが Access-Control-Allow-Origin 応答ヘッダで「このオリジンには読ませてよい」と宣言して初めて、ブラウザは中身を渡します。ヘッダが無ければ、通信は 200 でもブラウザ段で握りつぶされます。

ブロックしているのはブラウザであって、サーバーではない

リクエストはサーバーに到達し、サーバーは正常に応答しています。curl や Postman では成功するのに、ブラウザだと失敗するのはこのためです。エラーは「読み取り権限が無い」という意味で、サーバーが落ちているわけではありません。

横にスクロール

CORSエラーを事前確認と本要求の許可ヘッダーに分けて復旧確認する手順
サーバーの2xxだけでは成功ではない。ブラウザーが応答を公開できるヘッダーを両要求で確認する。

よくある原因(複数を具体的に、頻度順)

  1. サーバーが Access-Control-Allow-Origin を返していない(最多)。API 側で CORS 設定を一切していないケース。応答ヘッダにこの1行が無いだけでブロックされます。

  2. プリフライト(OPTIONS)が失敗しているContent-Type: application/json の POST、Authorization などのカスタムヘッダ、PUT/DELETE/PATCH などを使うと、ブラウザは本番リクエストの前に OPTIONS メソッドで「この操作を許可するか」を問い合わせます。これをプリフライトと呼びます。サーバーが OPTIONS に 2xx を返さない(404/405 を返す、認証ミドルウェアが先に 401 を返す等)と、本番リクエストは送られずブロックされます。

  3. credentials 併用時にワイルドカード(*)を使っている。クッキーや Authorization を送る(fetch(url, { credentials: 'include' }))場合、Access-Control-Allow-Origin: *仕様で禁止です。具体的なオリジンを1つ返し、かつ Access-Control-Allow-Credentials: true も返す必要があります。* のままだと The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*' when the request's credentials mode is 'include' が出ます。

  4. 許可ヘッダ/メソッドの宣言漏れ。プリフライトに対し Access-Control-Allow-HeadersAccess-Control-Allow-Methods が不足し、送ろうとしたヘッダ・メソッドがリストに無い。Request header field authorization is not allowed by Access-Control-Allow-Headers はこの典型です。

単純リクエスト(simple request)だけはプリフライトが省略される

GET/HEAD/POST で、Content-Type が text/plain application/x-www-form-urlencoded multipart/form-data のいずれか、かつカスタムヘッダが無い場合は「単純リクエスト」となりプリフライトが省かれます。それでも応答に Access-Control-Allow-Origin が無ければブロックされる点は同じです。

診断の手順(切り分け方・見るべきログ/コマンド)

まずサーバー自体が応答するかを、ブラウザ外から確認します。ここで応答が返るなら、問題はサーバーの可用性ではなく CORS ヘッダの有無に絞れます。

# 実オリジンを名乗って応答ヘッダを確認(-i でヘッダ表示)
curl -i -H "Origin: https://app.example.com" https://api.example.com/data

応答に Access-Control-Allow-Origin が含まれるかを見ます。無ければ原因1で確定です。次にプリフライトを手動で再現します。ブラウザが送るのと同じ OPTIONS を投げ、ステータスと許可ヘッダを確認します。

# プリフライトの再現:2xx と Allow-* が返るべき
curl -i -X OPTIONS https://api.example.com/data \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: content-type,authorization"

ブラウザ側では Network タブを開き、失敗したリクエストの直前に同名の OPTIONS リクエストが並んでいないかを確認します。並んでいればプリフライト方式です。その OPTIONS を選び、ステータスコードと Response Headers を読みます。以下の対応で原因が切り分けられます。

観察された状態推定原因確認すべきヘッダ
OPTIONS が無く本番だけ失敗単純リクエストで Allow-Origin 欠落Access-Control-Allow-Origin
OPTIONS が 404 / 405 / 401サーバーが OPTIONS を処理していないOPTIONS ルーティング・認証の順序
OPTIONS は 200 だが本番で失敗Allow-Headers / Allow-Methods 不足Access-Control-Allow-Headers / -Methods
wildcard の警告文が出るcredentials と * の併用Allow-Origin(具体値)+ Allow-Credentials

解決と予防(対処と再発防止)

直すのは応答を返すサーバー側です。API に CORS ミドルウェアを入れ、許可オリジン・メソッド・ヘッダを明示します。以下は Express の例ですが、考え方はどの言語でも同じです。

// Express: 具体的なオリジンを許可し、プリフライトにも応答する
const cors = require('cors');
app.use(cors({
  origin: 'https://app.example.com',   // credentials を使うなら * は不可
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,                    // クッキー等を送るとき
}));
// cors ミドルウェアが OPTIONS を自動応答する。認証ミドルウェアより前に置く

ミドルウェアを使わず自前で返す場合、プリフライトに必ず 2xx を返すこと、そして credentials を使うなら Allow-Origin に具体値を返すことが要点です。

# credentials 利用時に返すべき応答ヘッダの例
Access-Control-Allow-Origin: https://app.example.com   # * は不可
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 600                            # プリフライト結果をキャッシュ

複数オリジンを許可したいときは、* を返すのではなく、リクエストの Origin を許可リストと照合し、一致したものだけをそのまま返して Vary: Origin を付けます(キャッシュ汚染を防ぐため)。

CORS を『無効化』して回避しようとしない

ブラウザ拡張で CORS を切る、Access-Control-Allow-Origin: * を常時返す、といった対処は同一オリジンポリシーという防御そのものを外す行為で、CSRF や情報漏洩の穴になります。CORS は攻撃ではなく防御であり、正しく設定して通すのが解です。credentials を使う API で全オリジンを許可することは、特に避けてください。

なぜサーバー側で直すのか。ブラウザは「どのオリジンに読ませてよいか」を、応答を返すサーバーだけが正しく判断できると考えます。もしフロントの JavaScript で許可を上書きできてしまえば、攻撃者のページも同じ手で許可を偽装でき、防御が無意味になります。だから許可の宣言権はサーバーに固定されており、フロント側のコード変更では原理的に解決できません。

開発中に相手サーバーを直せない場合は、開発サーバーのプロキシ(Vite の server.proxy、webpack-dev-server の proxy、Next.js の rewrites 等)で API 呼び出しを同一オリジン経由に見せ、ブラウザに別オリジンと認識させない方法が定石です。これは一時回避であり、本番では上記のサーバー設定が必要です。

CORS の土台である同一オリジンポリシーやブラウザの挙動はWeb、HTTP メソッドとヘッダの仕組みはネットワーク、クッキー・CSRF との関係はセキュリティも参照してください。

試験・面接での頻出ポイント
  • CORS を実施するのはブラウザ。サーバーは通常どおり応答しており、ブロックはクライアント側で起きる
  • オリジン=スキーム・ホスト・ポートの3つ組。1つでも違えばクロスオリジン
  • プリフライト(OPTIONS)が発生する条件:単純リクエスト以外(JSON の POST、カスタムヘッダ、PUT/DELETE 等)
  • credentials: 'include' のとき Allow-Origin に * は使えず、具体オリジン+ Allow-Credentials: true が必須

エラー辞典の記事ガイド

CORSエラー(Access-Control-Allow-Origin が無い)を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

CORS

比較で見る軸

難易度: advanced / カテゴリ: エラー辞典 / タグ数: 6

導入後に効く点

主因は許可ヘッダ不足、OPTIONSが2xxを返さない、credentialsとワイルドカードを併用する3つ。ブラウザのNetworkでOPTIONSと応答ヘッダを確認する。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
エラー辞典
タグ数
6

判断チェックリスト

  • 自社の用途が「CORS / Web」に近いか確認する。
  • 強みである「CORS はブラウザが実施するクロスオリジン読み取りの安全機構で、サーバーが Access-Control-Allow-Origin 応答ヘッダで明示的に許可しない限り、レスポンスの中身を JavaScript に渡さずブロックする。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

CORSWebHTTPブラウザセキュリティ