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

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

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

目次(43項目)

先に要点

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

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

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

CORSとは何か

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

CORS はブラウザの仕組み

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

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

読み込み中...

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

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

たとえば、

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

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

このため、

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

オリジン = スキーム + ホスト + ポート https example.com 8443 スキーム ホスト ポート ローカル開発の例 フロント http://localhost:3000 API http://localhost:8000 ポートが違うので、ブラウザから見ると別オリジン

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

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

制限がなかったら

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

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

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

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

  • http://localhost:3000 の画面から http://localhost:8000/api を呼ぶ
  • https://app.example.com から https://api.example.com を呼ぶ
  • JavaScript の fetch() や Axios で別オリジンの API を叩く
応答をアプリへ渡さないのはブラウザ

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

まずよくある誤解

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

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

バックエンド処理と CORS 判定は別

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

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

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

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

fetch() の mode: 'no-cors' を見つけて、これで解決できると思う人も多いです。

no-cors は解決策ではない

このモードは 読める普通の API 応答を得る ための解決策ではありません。初心者が 画面から API の JSON を受け取りたい 場面では、ほぼ期待どおりに使えないと考えた方が安全です。

Preflight request とは何か

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

典型的には、次のような場面で、先に OPTIONS リクエストが飛びます。

Content-Type

application/json を付ける

ヘッダー

Authorization ヘッダーを付ける

メソッド

PUT PATCH DELETE を使う

Preflight request の流れ
  1. 1

    OPTIONS で確認する

    ブラウザが本番のリクエストの前に、このリクエスト送ってよい?とサーバーに確認する。

  2. 2

    サーバーが正しく返す

    この preflight に対してサーバーが正しく返せないと、ここで止まる。

  3. 3

    本リクエストへ進む

    preflight が通って、はじめて実際の本リクエストが送られる。

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

何を許可するのか

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

Origin

どの Origin からのアクセスを許可するか

メソッド

どの HTTP メソッドを許可するか

ヘッダー

どのヘッダーを許可するか

認証情報

特によく見るのが 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 エラー切り分けの6ステップ

  1. 1
    リクエスト元の画面URLを確認

    ブラウザのアドレスバーで、いまどのオリジン(scheme + host + port)から API を呼んでいるか確認する。

  2. 2
    呼んでいる API の URL を確認

    fetch / axios のコード、または DevTools の Network タブで、実際に叩いている API のオリジンを確認。

  3. 3
    オリジンが違うかを判定

    画面と API のオリジンが完全に一致していれば CORS の対象外。違うなら CORS チェックがかかる。サブドメイン違い・ポート違い・http/https 違いも別オリジン。

  4. 4
    OPTIONS(プリフライト)が飛んでいるか確認

    DevTools の Network で OPTIONS リクエストが先行しているか見る。プリフライトが失敗していると本リクエストが送られない。

  5. 5
    応答ヘッダーを確認

    Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Credentials の有無と値を確認。サーバー側で正しく返しているか。

  6. 6
    401/403/500 と CORS を混同しない

    認証エラーやサーバーエラーが CORS エラーに見えることがある。サーバー応答自体(status code)と、ブラウザの CORS 判定は別の問題として切り分ける。

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

実務でどう考えるか

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

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

構成

フロントと API を別ドメイン・別サブドメインで分ける

フレームワーク

Next.js や Nuxt の画面から別 API を呼ぶ

分離

管理画面と公開APIを分離する

認証

認証付きの API をブラウザから使う

CORS に関するよくある質問

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

認証の要らない公開データなら * が適切なこともありますが、認証が絡む API では使えません。MDN のとおり、Cookie などの認証情報を含むリクエストに * で応えると、ブラウザは応答の読み取りを止めます。また * は、どのサイトのスクリプトにも応答を読ませるという意味です。なお CORS は「応答を読ませるか」の仕組みで、リクエストそのものを止めるものではありません。認証が絡むなら、許可するオリジンを明示的に列挙 するのが安全です。

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

主な対象は fetch() や XMLHttpRequest など、スクリプトから出すクロスオリジンリクエストです。ほかにも MDN は、CSS の @font-face で読む Web フォント、WebGL のテクスチャ、canvas に drawImage() で描く画像や動画を挙げています。crossorigin 属性の付いた要素や type="module" のスクリプトも CORS で取得されます。一方、属性なしの画像や従来の <script> の読み込み自体は CORS の判定を受けません(中身をスクリプトから読むことはできません)。サーバー間通信(curl、Node.js のサーバー側)には CORS は関係しません。

Preflight(OPTIONS)はなぜ飛ぶ?

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

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

ローカル開発なら、Vite(server.proxy)や webpack-dev-server のプロキシ機能で /api/* を API サーバーへ転送すると、ブラウザから見て同じオリジンになり CORS が要りません。もう1つは、開発環境でもサーバー側で http://localhost:5173 などを明示的に許可する方法です。ブラウザのセキュリティ機能そのものを無効にする起動方法は、他のサイトに対する保護まで外れるので勧めません。

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

Express なら cors パッケージ、Laravel なら標準の HandleCors ミドルウェアと config/cors.php(Laravel 11 以降は php artisan config:publish cors で作成)、Django なら django-cors-headers、FastAPI なら CORSMiddleware、Spring Boot なら @CrossOrigin または WebMvcConfigurer。各フレームワークに公式 / 準公式のミドルウェアが用意されているので、ゼロから書く必要はほぼありません。

Next.js の Route Handlers(API Routes)に CORS は必要?

同じドメインで配信される画面から叩くなら不要。外部ドメインや別オリジンの SPA から叩くなら必要です。Next.js 16 では、Route Handler の中でヘッダーを返すか、proxy.ts(Next.js 15 までの middleware.ts。16 で改名)でまとめて設定します。公式ドキュメントに、許可するオリジンを照合する例があります。

まとめ

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

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

参考情報

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

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