プログラミング ソフトウェア 公開日 2026.04.04 更新日 2026.05.14

CORSとは?初心者がつまずきやすい原因と考え方をわかりやすく解説

CORSとは何か、なぜエラーになるのか、何を許可すべきなのか、初心者がつまずきやすい考え方を整理した記事です。

先に要点

  • CORS は、ブラウザが別オリジンへのリクエストをどう扱うかを決める仕組みです。
  • よくある CORS エラーは、API が壊れているというより `ブラウザが止めている` ケースがかなり多いです。
  • `とりあえず全部許可` は危ないので、どの Origin を、どのメソッドやヘッダーで許可するのか整理して考える必要があります。

フロントエンドから API を呼んだら CORS エラーが出たけど、何が悪いのか分からない というのはかなりよくあるつまずきです。
しかも、初心者のうちは サーバーエラーブラウザの制約 が混ざって見えやすいので、さらに分かりにくくなります。

この記事では、2026年4月4日時点で MDN の CORS ガイド、Preflight request の説明、WHATWG Fetch Standard の CORS まわりを確認しながら、CORS とは何か、なぜエラーになるのか、どう考えると整理しやすいのかを初心者向けにまとめます。
フロントエンドAPI を分ける構成の全体像から見たいなら、代表的なフレームワーク7選|Laravel・Django・Rails・Spring Boot・Next.js・Nuxt・FastAPIの向いている用途を比較 もつながりやすいです。

CORSとは何か

CORSCross-Origin Resource Sharing の略で、ブラウザが別オリジンへのリクエストをどう許可するかを決める仕組みです。
MDN でも、追加の HTTP ヘッダーによって、あるオリジンで動く Web アプリが別オリジンのリソースへアクセスできるかをブラウザへ伝える仕組みと説明されています。

ここで先に押さえたいのは、CORS はブラウザの仕組み だということです。 つまり、curl やサーバー間通信では通るのに、ブラウザだけ失敗することがあります。

次の図では、同一オリジン・別オリジン(単純リクエスト)・別オリジン(プリフライト)の3パターンで、ブラウザとサーバーの間で何が起きているかをステップごとに確認できます。

読み込み中...

そもそも「オリジン」とは何か

Origin は、ざっくり言うと スキーム + ホスト + ポート の組み合わせです。

たとえば、

  • https://example.com
  • https://api.example.com
  • http://example.com
  • https://example.com:8443

は、それぞれ別オリジンとして扱われることがあります。

このため、

のような開発構成でも、ブラウザから見ると別オリジンです。
ここで CORS が出やすくなります。

なぜそんな制限があるのか

背景にあるのは Same-Origin Policy です。
これは、あるサイト上で動くスクリプトが、別オリジンの応答を自由に読めないようにするブラウザの基本制約です。

もしこれがなければ、悪意あるサイトを開いただけで、別サイトの情報が勝手に読まれる危険が高くなります。
そのため、同一オリジン以外は原則そのまま読ませない をベースにして、必要なときだけ CORS で許可する形になっています。

どういうときに CORS エラーになるのか

初心者がハマりやすいのは、リクエスト自体は飛んでいるのに、ブラウザでだけ読めない パターンです。

たとえば次のような場面です。

  • http://localhost:3000 の画面から http://localhost:8000/api を呼ぶ
  • https://app.example.com から https://api.example.com を呼ぶ
  • JavaScriptfetch() や Axios で別オリジンの API を叩く

このとき、サーバーが適切な CORS ヘッダーを返していないと、ブラウザは応答をアプリ側へ渡しません。

まずよくある誤解

API が 200 を返していても CORS で失敗することがある

ここはかなり大事です。
ネットワークタブを見ると 200 に見えるのに、JavaScript 側ではエラーになることがあります。

これは、HTTP 応答そのものは返っていても、ブラウザが このオリジンには読ませない と判断しているからです。
つまり、バックエンド処理と CORS 判定は別の話です。

「サーバー間通信」には CORS は関係ない

サーバーからサーバーへ API を呼ぶだけなら、普通は CORS は関係しません。
ブラウザで動く JavaScript が別オリジンへ取りに行くときに問題になりやすいです。

no-cors を付ければ解決するわけではない

fetch()mode: 'no-cors' を見つけて、これで解決できると思う人も多いです。
ただ、このモードは 読める普通の API 応答を得る ための解決策ではありません。
初心者が 画面から API の JSON を受け取りたい 場面では、ほぼ期待どおりに使えないと考えた方が安全です。

Preflight request とは何か

ブラウザは、いきなり本番のリクエストを送る前に、このリクエスト送ってよい? と確認することがあります。
これが Preflight request です。

典型的には、

  • Content-Type: application/json を付ける
  • Authorization ヘッダーを付ける
  • PUT PATCH DELETE を使う

ような場面で、先に OPTIONS リクエストが飛びます。

この preflight に対してサーバーが正しく返せないと、実際の本リクエストまで進みません。
初心者が POST なのに OPTIONS が見える と戸惑いやすいのはこのためです。

何を許可するのか

CORS では、主に次を整理します。

  • どの Origin からのアクセスを許可するか
  • どの HTTP メソッドを許可するか
  • どのヘッダーを許可するか
  • Cookie や認証情報を含めるか

特によく見るのが Access-Control-Allow-Origin です。
これは、どのオリジンを許可するかをブラウザへ伝えるヘッダーです。

初心者向けの考え方

実務では、CORS を有効にする ではなく、どの画面からどの API を呼ばせたいのか を先に決めます。

たとえば、

なら、API 側で https://app.example.com を許可対象にします。

ここで雑に * を返すと、開発中は動いても、あとで認証や Cookie を含めたときに困りやすいです。
特に credentials: 'include' を使う構成では、ワイルドカードで済まないことがあります。

つまずきやすい原因

ポートが違う

localhostが同じでも、ポートが違えば別オリジン。ローカル開発でかなり多いです。

OPTIONSを処理していない

POSTは実装済みでもpreflight用のOPTIONS応答がなく、ブラウザだけ止まるパターンです。

許可オリジンの設定ミス

ワイルドカード(*)にしすぎてcredentialsとぶつかる、本番ドメインを入れ忘れるなど。

CORSと認証エラーが混ざる

401/403とCORSが同時に出ると原因が見えにくい。ネットワークタブで分けて見ます。

1. フロントと API のポートが違う

ローカル開発ではこれがかなり多いです。 localhost が同じでも、ポートが違えば別オリジンになることがあります。

2. preflight の OPTIONS を処理していない

本体の POST /api/... は作ったのに、OPTIONS への返しが足りず、ブラウザだけ止まるパターンです。

3. 許可オリジンを雑にしすぎる or 絞りすぎる

* にしてしまってあとで認証情報とぶつかる、逆に本番ドメインを入れ忘れる、というのはかなりよくあります。

4. CORS と認証エラーが混ざって見える

401 や 403 と CORS が同時に出ると、どちらが本当の原因か見えにくくなります。 まずはネットワークタブで、サーバー応答そのものブラウザの CORS 判定 を分けて見るのが大事です。

どう確認するとよいか

CORS エラーが出たら、6ステップで原因を切り分けると迷いにくくなります。

読み込み中...

この順に分けるだけで、かなり迷いにくくなります。

実務でどう考えるか

実務では、CORS は とりあえず動けばよい設定 にしない方が安全です。
フロントと API を分ける構成ではかなり普通に出てくるので、最初から 許可するオリジン を設計に入れておく方が後で崩れにくいです。

特に次のような場面で重要です。

  • フロントと API を別ドメイン・別サブドメインで分ける
  • Next.jsNuxt の画面から別 API を呼ぶ
  • 管理画面と公開APIを分離する
  • 認証付きの API をブラウザから使う

CORS に関するよくある質問

サーバー側Access-Control-Allow-Origin: * にすれば全部解決?

動くようには見えますが、推奨されません。* を返すと、認証情報(Cookie)を含むリクエストでは Access-Control-Allow-Credentials との組み合わせで拒否されますし、攻撃者を含むあらゆるサイトから API を叩かれます。許可するオリジンを明示的にリストアップ するのが安全です。

CORS は JavaScript からのリクエストだけが対象?

主に Fetch / XMLHttpRequest など スクリプトから出すクロスオリジンリクエスト が対象です。<img><link><script> タグからの単純なリソース取得は CORS の対象外(読み取り結果へのアクセス制限はあります)。サーバー間通信(curl、Node.js のサーバー側)には CORS は関係しません。

Preflight(OPTIONS)はなぜ飛ぶ?

単純リクエスト 以外(PUT/DELETE、カスタムヘッダー、application/json など)でブラウザが事前確認のために OPTIONS を投げます。サーバーが許可を返さないと本リクエストは送られません。preflight キャッシュ(Access-Control-Max-Age)を使えば、毎回 OPTIONS を飛ばさずに済みます。

開発中だけ CORS を無効にする方法は?

ローカル開発なら、Vite / webpack-dev-server のプロキシ機能を使って /api/* をサーバーへ転送する、もしくは Chrome を --disable-web-security で起動する(開発専用、本番では絶対に使わない)方法があります。一番安全なのは、開発環境でもサーバー側localhost:5173 などを明示的に許可することです。

サーバー側フレームワーク別の設定方法は?

Express なら cors パッケージ、Laravel なら config/cors.phpDjango なら django-cors-headersFastAPI なら CORSMiddlewareSpring Boot なら @CrossOrigin または WebMvcConfigurer。各フレームワークに公式 / 準公式のミドルウェアが用意されているので、ゼロから書く必要はほぼありません。

Next.js の API Routes に CORS は必要?

同じドメインで配信される画面から叩くなら不要。外部ドメインや別オリジンの SPA から叩くなら必要です。Next.js では middleware.ts か API Route 内で手動でヘッダーを設定するか、専用のライブラリを使います。

まとめ

CORS は、ブラウザが別オリジンのリクエストをどう扱うかを決める仕組みです。 大事なのは、CORS エラーは API が壊れた のではなく、ブラウザが読ませていない ことが多いと分けて考えることです。

そのうえで、OriginSame-Origin PolicyPreflight request を押さえると、かなり見通しがよくなります。
初心者のうちは、どの画面からどの API を呼ばせたいのか を先に整理して、サーバー側で必要な範囲だけ許可する考え方で進めるのがいちばん分かりやすいです。

参考情報

あとで見返すならここで保存

読み終わったあとに残しておきたい記事は、お気に入りからまとめて辿れます。