- CORSエラーは、ブラウザから別オリジンのAPIにfetchでアクセスした際、サーバー側の応答に許可情報がないと発生するエラーだ。
- 実務ではサーバーがAccess-Control-Allow-Originヘッダーを返していないケースが原因の大半を占める。
- 解決にはNetworkタブでレスポンスヘッダーを確認し、サーバー側でCORS設定を追加するか、開発環境ではプロキシを使えばいい。
漫画で描かれているように、CORSエラーはブラウザの開発者コンソールに突然赤い文字で表示されるため、多くの開発者が最初は原因をつかめず動揺します。しかし実際にはサーバーがAccess-Control-Allow-Originヘッダーを返していないという単純な原因であるケースが大半を占めています。
厄介なのは、このエラーがNetworkタブ上ではリクエストが成功しているように見える点です。サーバー側の処理自体は正常に完了しているのに、レスポンスを受け取ったブラウザだけが同一オリジンポリシーに基づいて読み取りを拒否します。原因の切り分けを誤ると、サーバーのログを延々と調べて時間を浪費することになりかねません。
放置したまま本番環境にリリースすると、ユーザーの画面ではデータが一切表示されないまま真っ白になったり、フォーム送信が静かに失敗したりする恐れがあります。開発初期の段階でCORS設定を確認する習慣をつけておくことが、こうした障害を未然に防ぐ近道になります。
- JavaScript CORSエラーの基本情報
- なぜJavaScriptでCORSエラー(has been blocked by CORS policy)が発生するのか?
- CORSエラーはブラウザによって表示されるメッセージが違う?
- ReactやNext.jsでCORSエラーが起きやすいのはなぜ?
- Redirect is not allowed for a preflight requestなど別パターンのCORSエラーはどう対処する?
- CORSエラーはどうデバッグすればいい?
- CORSエラーを未然に防ぐには?
- no-corsモードを使えばCORSエラーは解決する?
- よくある質問
- この記事と一緒に知っておきたいエラー解決
- 【出典】参考URL
JavaScript CORSエラーの基本情報
JavaScript CORSエラーは、ブラウザのfetch APIやXMLHttpRequestで別オリジンのAPIにアクセスした際、サーバーの応答に必要なヘッダーが含まれていないためにレスポンスの読み取りが拒否される現象です。エラーメッセージの先頭はAccess to fetchで始まるのが特徴です。
| 項目 | 内容 |
|---|---|
| エラーメッセージ | Access to fetch at ‘(リクエスト先URL)’ from origin ‘(自分のオリジン)’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. |
| 発生する言語・環境 | JavaScript(ブラウザ)/Chrome・Edge等のChromiumベースブラウザ、Fetch APIやXMLHttpRequestを使った通信全般 |
| エラーの意味 | 指定したURLへのfetchが、CORSポリシーによってブロックされました。要求したリソースに’Access-Control-Allow-Origin’ヘッダーが存在しません、という意味。ブラウザの同一オリジンポリシーがレスポンスの読み取りを拒否したことを示す。 |
| 主な原因 | アクセス先サーバーがAccess-Control-Allow-Originヘッダーを返していないこと |
| 解決の基本方針 | Networkタブでレスポンスヘッダーを確認し、サーバー側にCORS設定を追加するか、開発環境ではプロキシ機能を使う |
なぜJavaScriptでCORSエラー(has been blocked by CORS policy)が発生するのか?
CORSエラーの原因は、リクエストが処理される流れのどこで許可情報が欠けているかによって整理できます。以下では発生しやすい3つの箇所を、実務での遭遇頻度が高い順に見ていきます。
レスポンスにAccess-Control-Allow-Originヘッダーがない
もっとも多いのは、アクセス先のサーバーがCORSに関する設定を何も行っておらず、レスポンスにAccess-Control-Allow-Originヘッダーが含まれていないケースです。
// フロントエンド (Vite 5 + React 18) - http://localhost:5173 で動作
fetch('https://api.example.com/users')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
// このfetch自体は正しく書けているが、次のサーバー側の設定が原因でブロックされる
// バックエンド (Node.js 20 + Express 4) - CORS関連の設定を何も行っていない
const express = require('express');
const app = express();
app.get('/users', (req, res) => {
res.json([{ id: 1, name: 'Taro' }]);
// Access-Control-Allow-Origin ヘッダーを一切返していないため
// ブラウザ側でレスポンスの読み取りがブロックされる
});
app.listen(4000);
サーバー側にcorsパッケージを導入し、許可するオリジンを明示するだけでAccess-Control-Allow-Originヘッダーが自動的に付与されるようになります。フロントエンドのコードは変更不要です。
// バックエンド (Node.js 20 + Express 4 + corsパッケージ)
const express = require('express');
const cors = require('cors');
const app = express();
// 許可するオリジンを明示し、Access-Control-Allow-Originを自動付与する
app.use(cors({ origin: 'http://localhost:5173' }));
app.get('/users', (req, res) => {
res.json([{ id: 1, name: 'Taro' }]);
});
app.listen(4000);
フロントとバックエンドでポート番号が違う開発環境
開発環境ではlocalhost同士で通信していても、ポート番号が異なるだけで別オリジンとみなされます。ホスト名が同じだから大丈夫、という思い込みでハマりやすいパターンです。
// フロントエンド (Vite 5) は http://localhost:5173 で起動
// バックエンドAPIは http://localhost:8080 で起動している
fetch('http://localhost:8080/api/profile')
.then(res => res.json())
.then(data => setProfile(data));
// ホスト名が同じlocalhostでも、ポート番号が異なると別オリジン扱いになる
開発環境ではサーバー側を直接呼ばず、ビルドツールのプロキシ機能を使ってフロントから見た通信先を同一オリジンに揃える方法がよく使われます。
// vite.config.js にプロキシを設定し、フロントから見ると同一オリジンにする
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
};
// fetch側はプロキシ経由の相対パスに書き換える
fetch('/api/profile')
.then(res => res.json())
.then(data => setProfile(data));
プリフライトリクエスト(OPTIONS)がブロックされている
PUTやDELETEなどのメソッド、またはAuthorizationのようなカスタムヘッダーを使うと、ブラウザは本リクエストの前にOPTIONSメソッドでプリフライトリクエストを自動送信します。このプリフライトへの応答が不足しているとエラーになります。
// PUTメソッドとAuthorizationヘッダーを使うため
// ブラウザは本リクエストの前にOPTIONSでプリフライトを自動送信する
fetch('https://api.example.com/orders/1', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer abc123',
},
body: JSON.stringify({ status: 'shipped' }),
});
// サーバーがOPTIONSに未対応、またはAccess-Control-Allow-Methods /
// Access-Control-Allow-Headers が不足していると、プリフライトの時点でブロックされる
corsパッケージでメソッドと許可ヘッダーを明示すれば、プリフライトへの応答も自動化されます。
// バックエンド (Node.js 20 + Express 4 + corsパッケージ)
const cors = require('cors');
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
}));
// corsミドルウェアがOPTIONSへの応答と各ヘッダーの付与を自動化する
app.put('/orders/:id', (req, res) => {
res.json({ status: 'updated' });
});

このエラー、赤字で表示されるだけに焦りますよね。でも落ち着いてNetworkタブを見れば、意外とシンプルな原因であることが多いです。
CORSエラーはブラウザによって表示されるメッセージが違う?
JavaScript CORSエラーの文言はブラウザエンジンによって微妙に異なります。原因は同じでも表示されるテキストが違うため、検索する際は自分のブラウザに合った文言で調べると情報にたどり着きやすくなります。
| 実行環境 | 表示されるエラーメッセージ |
|---|---|
| Chrome・Edge(Chromiumベースブラウザ) | Access to fetch at ‘(URL)’ from origin ‘(オリジン)’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. |
| Firefox | Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at (URL). (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: (ステータスコード). |
| Safari(WebKitベース) | [Error] Origin (オリジン) is not allowed by Access-Control-Allow-Origin. |
ReactやNext.jsでCORSエラーが起きやすいのはなぜ?
Reactでの発生パターン
Create React AppやViteで作ったReactアプリは、開発サーバーとAPIサーバーが別プロセスで動くため、コンポーネントから外部APIへ直接fetchするとCORSエラーに遭遇しやすくなります。開発時はビルドツールのプロキシ機能を使うのが定番です。
// Create React App (react-scripts 5) の場合は package.json に proxy を追加するだけでよい
// package.json
// {
// "proxy": "http://localhost:8080"
// }
// コンポーネント側は相対パスでfetchする
useEffect(() => {
fetch('/api/profile')
.then(res => res.json())
.then(data => setProfile(data));
}, []);
Next.jsでの発生パターン
Next.jsではRoute Handler(旧API Routes)を経由して外部APIを呼び出すと、サーバー同士の通信になるためブラウザのCORS制約自体を受けなくなります。フロントから直接外部APIを叩いて詰まっている場合の定番の回避策です。
// Next.js 14 (App Router) の Route Handler を経由してAPIを叩く
// app/api/proxy/route.js
export async function GET() {
// サーバー間の通信はブラウザのCORS制約を受けない
const res = await fetch('https://api.example.com/users');
const data = await res.json();
return Response.json(data);
}
// クライアントコンポーネント側は自ドメインのAPI Routeを呼ぶだけでよい
fetch('/api/proxy')
.then(res => res.json())
.then(data => console.log(data));
Redirect is not allowed for a preflight requestなど別パターンのCORSエラーはどう対処する?
Redirect is not allowed for a preflight request と表示される場合
プリフライトのOPTIONSレスポンスは200番台のステータスコードで返す必要があり、リダイレクトは許可されません。エンドポイントの末尾スラッシュ有無でサーバーがリダイレクトを返している場合に発生しやすいパターンです。
// 末尾のスラッシュ有無でリダイレクトが発生するAPIエンドポイント
fetch('https://api.example.com/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ item: 'book' }),
});
// サーバーが /orders を /orders/ へ301リダイレクトしていると
// プリフライトのOPTIONSリクエストがリダイレクトとして返され、ブロックされる
// 解決策: リダイレクトされない正しいパスに最初からリクエストする
fetch('https://api.example.com/orders/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ item: 'book' }),
});
must not be the wildcard ‘*’ と表示される場合
Cookieなどの認証情報を送るcredentials: 'include'を指定したリクエストでは、サーバーのAccess-Control-Allow-Originにワイルドカード*を使うことができません。具体的なオリジンを1つ指定する必要があります。
// Cookieを送信するためcredentials: 'include' を指定している
fetch('https://api.example.com/mypage', {
credentials: 'include',
});
// サーバーが Access-Control-Allow-Origin: * を返していると
// credentials込みのリクエストでは許可されずブロックされる
// サーバー側 (Express + cors) を修正
app.use(cors({
origin: 'https://example.com', // ワイルドカードではなく具体的なオリジンを指定
credentials: true, // Access-Control-Allow-Credentials: true を付与
}));
CORSエラーはどうデバッグすればいい?
CORSエラーはJavaScriptのコード側から詳細な理由を取得できない仕様になっているため、まずブラウザの開発者ツールのNetworkタブでレスポンスヘッダーを直接確認するのが基本です。加えて、curlでブラウザを介さずリクエストを送ってみると、サーバー側の応答そのものを切り分けられます。
# ターミナルでcurlを使い、ブラウザを介さずレスポンスヘッダーを直接確認する (curl 8系)
curl -i -H "Origin: https://example.com" https://api.example.com/users
# レスポンスヘッダーに Access-Control-Allow-Origin が含まれているかを確認する
# 含まれていなければサーバー側の設定不足が原因と断定できる




Networkタブだけでなく、リクエストがそもそもプリフライトなのか本リクエストなのかを見分けるのもコツです。一つずつ確認していきましょう。
CORSエラーを未然に防ぐには?
CORSエラーは開発の終盤で発覚すると手戻りが大きくなりがちです。API設計の初期段階で許可オリジンの一覧を決め、サーバー側の設定を一箇所にまとめておくと、あとから追加・変更するときの漏れを防ぎやすくなります。
// バックエンドでオリジンを一元管理し、環境ごとに切り替える (Node.js 20 + Express 4)
const allowedOrigins = [
'https://example.com',
'http://localhost:5173', // 開発環境用
];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
}));
no-corsモードを使えばCORSエラーは解決する?
fetchのオプションにmode: 'no-cors'を指定すると、コンソール上の赤いエラーは確かに消えます。しかしこれはCORSエラーの解決ではなく、レスポンスを不透明(opaque)な状態で受け取っているだけで、JavaScriptからレスポンスの中身やステータスコードを読み取ることはできません。
ログ送信のようにレスポンス内容を使わない一方向の通信であれば実用上問題になりませんが、APIのデータを画面に表示したい用途では使えません。エラーが消えたからといって安易にこのモードへ切り替えると、データが取得できていないことに気づかないまま本番運用してしまう危険があります。




原因がわかりにくいエラーですが、仕組みを理解すれば対処のパターンは限られています。落ち着いて一つずつ潰していきましょう。
よくある質問
-
Q本番環境でだけCORSエラーが発生するのはなぜですか?
-
A
開発環境ではビルドツールのプロキシ機能によって同一オリジン扱いになっていたリクエストが、本番ビルドではフロントの配信ドメインとAPIのドメインが実際に分かれるため、CORS制約が初めて表面化するからです。デプロイ前に本番相当のドメイン構成で動作確認しておくことが重要になります。
-
QNext.jsでAPI Routesを経由すればCORSエラーは必ず解消されますか?
-
A
サーバー間通信になるためブラウザのCORS制約自体は受けなくなりますが、外部API側の認証やレート制限まで肩代わりできるわけではありません。Route Handler内でのエラーハンドリングは別途実装しておく必要があります。
-
QESLintのようなツールでCORSエラーを事前に検出できますか?
-
A
CORSはサーバーとブラウザの間で実行時に発生する現象のため、静的解析だけでは検出できません。CI上でOriginヘッダーを付けたcurlリクエストを実行し、レスポンスヘッダーを検証するスモークテストを組み込んでおくと、事前に気づきやすくなります。
-
QCORSエラーが起きたときユーザーにはどう表示すればいいですか?
-
A
fetchのcatchブロックで通信エラーを検知し、通信環境をご確認くださいのような汎用的な文言に置き換えるのが基本です。CORSの技術的な詳細をそのままユーザー画面に出すことは避け、詳細はログ収集ツール側で確認できるようにしておきます。
-
QCORSエラーとJavaScript TypeError: Failed to fetchの違いは何ですか?
-
A
CORSエラーはサーバーがレスポンスを返しているのにブラウザが読み取りを拒否する現象であるのに対し、Failed to fetchはネットワーク断や証明書エラーなどでリクエスト自体が成立しなかった場合に発生します。Networkタブでレスポンスが返っているかどうかを確認すれば、どちらの現象かを切り分けられます。
この記事と一緒に知っておきたいエラー解決
| 関連エラー | この記事との関連 |
|---|---|
| JavaScript TypeError: Failed to fetch | CORSエラーとよく混同される、リクエスト自体が成立しない別種のfetchエラーとの切り分けに役立つ。 |
| JavaScript Unhandled Rejection (Promise error) | CORSエラーでfetchのPromiseがcatchされないまま未処理の拒否として現れるケースを補足できる。 |
| JavaScript SyntaxError: Unexpected token ‘<‘ in JSON | CORS設定を直した後もサーバーがエラーページ(HTML)を返していると、fetchのjson()解析で発生しやすい後続エラー。 |
| JavaScript TypeError: Cannot destructure property of null | CORSエラーでレスポンスを取得できずnullのままプロパティを分割代入してしまった際に発生しやすい。 |
| JavaScript TypeError: Cannot read properties of undefined | CORSエラーでデータ取得に失敗した後、未定義の値を参照して二次的に発生しやすいエラー。 |
【出典】参考URL
https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS:CORSの仕組み、プリフライトリクエスト、Access-Control-Allow-Originの基本仕様
https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors:ブラウザに表示される各種CORSエラーメッセージの一覧
https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors/CORSMissingAllowOrigin:Access-Control-Allow-Originヘッダー欠落時のエラー文言と対処法
https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin:Access-Control-Allow-Originヘッダーの仕様

コメント