CORS(オリジン間リソース共有)とは?仕組みやエラー対処法を解説

CORS(オリジン間リソース共有)とは?仕組みやエラー対処法を解説

PR Amazonのアソシエイトとして、ITナレッジライフは適格販売により収入を得ています。

記事の文字数:7,566

CORS(Cross-Origin Resource Sharing:オリジン間リソース共有)の仕組みを初心者にもわかりやすく解説。同一生成元ポリシー(SOP)との違い、プリフライトリクエストの流れ、代表的なレスポンスヘッダーの設定方法、よくあるCORSエラーの原因と解決策まで徹底網羅します。

ITエンジニアにお勧めの本 ↗

おすすめ

エンジニアが知っておきたい思考の整理術 複雑な情報を【理解する】【伝える】テクニック

難易度
実用性
図解

思考の整理というテーマを、実務ですぐに使える具体的な「技術」として落とし込んだ一冊です。

ベストセラー

世界一流エンジニアの思考法

難易度
実用性
思考法

技術スキル以上に大切な「考え方」をアップデートできる。世界基準の効率的な仕事術が学べます。

文系社会人のためのIT資格の歩き方: 文系のあなたに最適な道をご提案します (YKcreate kiki)

難易度
実用性
キャリア度

ロードマップが非常に明確。キャリアプランを見据えた、無駄のない学習順序を提案してくれます。

エンジニアを説明上手にする本 相手に応じた技術情報や知識の伝え方

難易度
実用性
コミュ力

「伝える技術」もエンジニアの重要なスキル。相手の視点に立った、具体的な説明のコツが満載です。

技術広報入門 ー テックブログから始めるエンジニアカルチャーのつくり方

難易度
実用性
独自性

テックブログを入口にエンジニア組織の文化をどう設計し、育てていくかという実践論が学べます。

Webアプリケーションを開発していると、フロントエンド(ReactReact [リアクト]Meta開発のUIライブラリやVueVue [ビュー]JavaScriptのプログレッシブWebフレームワークなど)からバックエンドのAPIAPI [エーピーアイ]Application Programming Interface。異なるソフトウェア間の接続口を呼び出した際に「has been blocked by CORS policy」というエラーに直面することがよくあります。

この記事では、Webエンジニアや基本情報技術者試験・応用情報技術者試験の受験者が押さえておくべき「CORSCORS [コース / シーオーアールエス]Cross-Origin Resource Sharing。クロスオリジンのアクセス制御(Cross-Origin Resource Sharing:オリジン間リソース共有)」の基本概念、ブラウザの同一生成元ポリシー(SOP)との関係、プリフライトリクエストの通信フロー、主要なレスポンスヘッダー、そして実践的なエラー解決策までを分かりやすく解説します。

CORS(オリジン間リソース共有)とは?

CORS(Cross-Origin Resource Sharing、オリジン間リソース共有)とは、あるオリジン(Webページ)で動作しているWebブラウザ上のスクリプトから、異なるオリジンのリソースにアクセスする際に、サーバーがHTTPHTTP [エイチティーティーピー]HyperText Transfer Protocol。Webでのデータ転送プロトコルレスポンスヘッダーを通じてブラウザにレスポンスの共有を許可する仕組みです。CORSはHTTPヘッダーを利用した仕組みであり、現在はWHATWGのFetch Standardで仕様が定義されています。

Webブラウザには、悪意のあるWebサイトからユーザーの情報を守るために「同一生成元ポリシー(Same-Origin Policy: SOP)」というセキュリティ機能が備わっています。SOPは、異なるオリジン間の通信をすべて禁止するものではなく、原則として、あるオリジンで動作するJavaScriptが別のオリジンのリソースやレスポンスの内容を自由に読み取ることを制限する仕組みです。

しかし、現代のWeb開発では「フロントエンド(frontend.example.com)」と「バックエンドAPI(api.example.com)」でドメインを分離したり、外部のSaaSSaaS [サース / サーズ]Software as a Service。クラウド経由でソフトウェアを提供するサービス形態 APIを利用したりする構成が一般的です。

CORSは、このように異なるオリジン間で安全にレスポンスを共有するために、サーバー側から「このオリジンへのレスポンス共有を許可します」という明示的な許可をブラウザに伝える仕組みです。

sequenceDiagram autonumber participant Browser as ブラウザ(example.com) participant API as APIサーバー(api.other.com) Browser->>API: HTTPリクエスト送信(Origin: httpsHTTPS [エイチティーティーピーエス]HTTP Secure。暗号化されたHTTP://example.com) API-->>Browser: HTTPレスポンス(Access-Control-Allow-Origin: https://example.com) Note over Browser: レスポンスヘッダーを確認
許可されているためJavaScriptにデータを渡す

オリジン(Origin)の定義と同一生成元ポリシー(SOP)

CORSを正しく理解するためには、前提となる「オリジン」と「同一生成元ポリシー」の概念を把握することが不可欠です。

オリジンを構成する3要素

Webにおける「オリジン(Origin)」とは、URLURL [ユーアールエル]Uniform Resource Locator。Webページのアドレスの以下の3つの組み合わせによって決定されます。

  1. スキーム(プロトコル): http、https など
  2. ホスト(ドメイン): example.com、localhost など
  3. ポート番号: 80、443、3000 など(明示されていない場合はスキームの既定ポート)

この 3要素がすべて完全一致する場合のみ「同一オリジン(Same-Origin)」 と判定され、1つでも異なれば「クロスオリジン(Cross-Origin)」となります。

基準URLを https://example.com:443/app/page.html とした場合の比較例は以下の通りです。

比較対象のURL判定異なる要素
https://example.com/api/users同一オリジンなし(ポート443はHTTPSの既定値で一致)
http://example.com/api/usersクロスオリジンプロトコル(HTTP と HTTPS)
https://api.example.com/usersクロスオリジンホスト(サブドメインが異なる)
https://example.com:8080/usersクロスオリジンポート番号(443 と 8080)
https://other.com/usersクロスオリジンホスト(ドメインが異なる)

同一生成元ポリシー(Same-Origin Policy: SOP)の目的

同一生成元ポリシー(SOP)は、ブラウザが持つもっとも基本的なセキュリティ境界です。SOPは、異なるオリジン間の通信をすべて禁止するものではなく、原則として、あるオリジンで動作するJavaScriptが別のオリジンのリソースやレスポンスの内容を自由に読み取ることを制限する仕組みです。

例えば、攻撃者が作成した悪意のあるWebサイト(evil.example.com)から、ユーザーがログイン中の銀行サイト(my-bank.example.com)に対してリクエストを送信できる場合があります。しかし、SOPによって、そのレスポンスの内容(個人情報や口座残高など)を攻撃者側のJavaScriptから読み取ることは原則として制限されます。 例えば、攻撃者が作成した悪意のあるWebサイト(evil.attacker.com)から、ユーザーがログイン中の銀行サイト(my-bank.example.com)に対してリクエストを送信できる場合があります。しかし、SOPによって、そのレスポンスの内容(個人情報や口座残高など)を攻撃者側のJavaScriptから読み取ることは原則として制限されます。

なお、ユーザーの意図しない送金リクエストなどを防ぐためには、SOPやCORSだけでなく、CSRFCSRF [シーサーフ / シーエスアールエフ]Cross-Site Request Forgery。偽造されたリクエストによる攻撃トークンやCookieの SameSite 属性などを適切に利用する必要があります。 なお、ユーザーの意図しない送金リクエストなどを防ぐためには、SOPやCORSだけでなく、CSRFトークンやCookieの SameSite 属性などを適切に利用する必要があります。ただし、Cookieの SameSite 属性はオリジンではなく「サイト」単位で判定されるため、同一サイト内の別サブドメイン(例:sub.example.com と my-bank.example.com)からの攻撃には十分な防御にならない場合があります。特にサブドメインを複数運用している場合は、CSRFトークンなどの対策も併用することが重要です。

CORSの仕組み:単純リクエストとプリフライトリクエスト

ブラウザがクロスオリジン通信を行う際、リクエストの内容に応じて「単純リクエスト」と「プリフライトリクエスト」の2通りの通信手順に分かれます。

1. 単純リクエスト(Simple Request)

ブラウザがプリフライトリクエストを必要としない特定の条件を満たしたHTTPリクエストを、一般に「単純リクエスト(Simple Request)」と呼びます。なお、単純リクエストは副作用の有無によって分類されるものではなく、例えば POST リクエストであってもサーバー側でデータを更新することは可能です。

単純リクエストとみなされる主な条件

  • HTTPメソッド: GET、POST、HEAD のいずれか
  • リクエストヘッダー: ユーザーエージェントが自動設定するヘッダーに加え、CORSセーフリストに含まれる以下のヘッダーのみを使用していること
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
    • Range (ただし、各ヘッダーには値に関する追加条件があり、たとえば Range は単一のバイト範囲を指定する形式である必要があります。また、独自のカスタムヘッダーなどセーフリストに含まれないヘッダーを使用する場合は、原則としてプリフライトリクエストが必要になります)
  • Content-Type の値: 以下の3つのMIMEタイプのいずれかに限定
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

現代のWebアプリで広く使われる application/json のPOSTリクエストは、単純リクエストには該当せず、後述するプリフライトリクエストの対象となります。

単純リクエストの流れ

  1. ブラウザがリクエストヘッダーに Origin: https://client.example.com を付加してサーバーへ送信。
  2. サーバーがレスポンスヘッダーに Access-Control-Allow-Origin: https://client.example.com(または *)を含めて返信。
  3. ブラウザがレスポンスヘッダーを確認し、送信元オリジンが許可されていれば、JavaScript側にレスポンスデータを引き渡す。

2. プリフライトリクエスト(Preflight Request)

単純リクエストの条件を満たさない場合(PUT、DELETE メソッド、application/json の送信、カスタムヘッダーの付加など)、ブラウザは実際のリクエストを送信する前に OPTIONS メソッドを使用してサーバーに事前確認を行います。この事前確認では、実際に使用するHTTPメソッドやリクエストヘッダーがCORSのルール上許可されているかを確認します。これを「プリフライトリクエスト(Preflight Request)」と呼びます。

sequenceDiagram autonumber participant Browser as ブラウザ(client.example.com) participant Server as APIサーバー(api.example.com) Note over Browser: 【1】プリフライトリクエスト(事前確認) Browser->>Server: OPTIONS /api/data
Origin: https://client.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization Note over Server: 許可条件をチェック Server-->>Browser: 204 No Content
Access-Control-Allow-Origin: https://client.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400 Note over Browser: 【2】実際のリクエスト(本番通信) Browser->>Server: POST /api/data
Origin: https://client.example.com
Content-Type: application/jsonJSON [ジェイソン]JavaScript Object Notation。軽量データ交換フォーマット
{"name": "Alice"} Server-->>Browser: 200 OK
Access-Control-Allow-Origin: https://client.example.com
{"status": "success"}

プリフライトが成功(HTTPステータスコード 200 や 204 などで適切なヘッダーが返却)して初めて、ブラウザは本番のHTTPリクエストを発行します。

なお、プリフライトリクエスト(OPTIONS)には、通常 Cookie などの認証情報は含まれません。そのため、認証が必要なAPIであっても、OPTIONS リクエスト自体に認証を必須としてしまうとプリフライトが失敗してしまいます。サーバー側では、認証前の段階で OPTIONS リクエストに対して適切にCORSヘッダーを返せるように設定しておく必要があります。

[!NOTE] HTTPステータスコードの意味や分類については、関連記事の HTTPステータスコード一覧と見分け方 で詳しく解説しています。

CORSを制御する主要なHTTPレスポンスヘッダー

CORSを許可するために、APIサーバー側がレスポンスとして返すべき重要なヘッダーは以下の通りです。

1. Access-Control-Allow-Origin

アクセスを許可するオリジンを指定します。

Access-Control-Allow-Origin: https://frontend.example.com

特定のオリジン1つ、または不特定多数に公開する場合はワイルドカード(*)を指定できます。

2. Access-Control-Allow-Methods

プリフライトリクエストに対して、許可するHTTPメソッドの一覧を指定します。

Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS

3. Access-Control-Allow-Headers

実際のリクエストで使用を許可するHTTPリクエストヘッダーを指定します(Authorization や Content-Type など)。

Access-Control-Allow-Headers: Content-Type, Authorization

4. Access-Control-Allow-Credentials

Cookieなどの認証情報を含むクロスオリジンリクエストについて、ブラウザがレスポンスの共有を許可するかどうかを指定します。Access-Control-Allow-Credentials: true を指定すると、認証情報を含むリクエストに対して、条件を満たしたレスポンスをブラウザがJavaScriptに公開できるようになります。

Access-Control-Allow-Credentials: true

なお、開発者が明示的に設定する Authorization ヘッダーはCORS上のリクエストヘッダーとして扱われるため、Access-Control-Allow-Credentials: true を設定するだけでは許可されません。Authorization ヘッダーを使用する場合は、プリフライトレスポンスの Access-Control-Allow-Headers にも Authorization を明示的に指定する必要があります。

[!IMPORTANT] Cookieなどの認証情報を含むリクエスト(Fetch APIなどで credentials: 'include' を指定した場合など)では、Access-Control-Allow-Origin にワイルドカード(*)を使用できません。必ず具体的なオリジン(例: https://frontend.example.com)を明示し、あわせてレスポンスに Access-Control-Allow-Credentials: true を設定する必要があります。

5. Access-Control-Max-Age

プリフライトリクエストの結果をブラウザがキャッシュする秒数を指定します。指定した秒数の間は、キャッシュが有効な範囲で同じ条件のプリフライト通信が省略され、パフォーマンスの向上につながります。

Access-Control-Max-Age: 86400

ただし、実際のキャッシュ保持時間にはブラウザごとの上限があり、指定した秒数より短くなる場合があります。

6. Access-Control-Expose-Headers

クロスオリジン通信のレスポンスに含まれるHTTPヘッダーのうち、JavaScriptから参照可能にするものを指定します。デフォルトではすべてのレスポンスヘッダーをJavaScriptから読み取れるわけではないため、API独自のカスタムヘッダーなどを参照したい場合に設定します。

Access-Control-Expose-Headers: X-Request-ID

これにより、フロントエンド側で次のように取得できます。

const response = await fetch('https://api.example.com/users');
const requestId = response.headers.get('X-Request-ID');

開発現場で頻出するCORSエラーの原因と解決策

開発者が遭遇する典型的なエラーメッセージは以下の通りです。

Access to fetch at 'https://api.example.com/users' from origin 'https://localhost:3000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.

よくある誤解:ブロックしているのは「サーバー」ではなく「ブラウザ」

CORSエラーが発生したとき、「サーバー側がリクエストを拒否して通信エラーを返してきた」と誤解されがちです。

しかし実際には、サーバーが正常にレスポンス(200 OKなど)を返しているにもかかわらず、必要なCORSヘッダーが含まれていないため、ブラウザ側がJavaScriptへのレスポンスの引き渡しをブロックしているケースがあります。

また、これ以外にも以下のような原因でCORSエラーが発生することがあります。

  • プリフライトリクエストに対して適切なレスポンスが返ってこない
  • 許可されていないHTTPメソッドやリクエストヘッダーが指定されている
  • 認証情報を含むリクエストに対して、適切なCORSヘッダーが返っていない
  • サーバーエラー(500系など)のレスポンスやリダイレクト先のレスポンスにCORSヘッダーが付与されていない

なお、curl コマンドやPostman、バックエンド同士の通信でCORSエラーが発生しないのは、これらがWebブラウザによるCORSの制限を受けないためです(もちろん、HTTPエラーや認証エラー自体は発生します)。

解決策1: バックエンドサーバー側でCORSヘッダーを正しく設定する

もっとも根本的かつ正しい解決策は、APIサーバー側で許可するオリジンをホワイトリスト方式で設定することです。

Node.js(Express)の場合

cors ミドルウェアを利用して明示的に設定します。

import express from 'express';
import cors from 'cors';
const app = express();
const allowedOrigins = ['https://myapp.example.com', 'http://localhost:3000'];
app.use(cors({
origin: (origin, callback) => {
// OriginがないリクエストはCORSの制限対象外。
// Originがある場合は許可リストと照合する。
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('CORS policy: Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
app.get('/api/users', (req, res) => {
res.json({ users: [{ id: 1, name: 'Taro' }] });
});
app.listen(8000, () => {
console.log('Server running on port 8000');
});

なお、CORSはあくまでブラウザ上のアクセス制御の仕組みであり、API自体のアクセス制御ではありません。Origin がないリクエストも拒否したい場合などは、API側で別途認証・認可を実装してください。

Python(FastAPI)の場合

FastAPIに標準用意されている CORSMiddleware を導入します。allow_credentials=True を指定する場合は、allow_origins だけでなく allow_methods や allow_headers にもワイルドカード(*)を使わず、許可するメソッド・ヘッダーを明示します。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"https://myapp.example.com",
"http://localhost:3000",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["Content-Type", "Authorization"],
)
@app.get("/api/users")
def get_users():
return {"users": [{"id": 1, "name": "Taro"}]}

解決策2: 開発環境ではリバースプロキシを活用する

外部の公開APIや、バックエンドのコードを直接変更できないサードパーティ製サーバーと通信する場合、クライアント(フロントエンド開発環境)側でプロキシを設定し、同一オリジン経由の通信に見せかける手法が効果的です。

例えば、ViteVite [ヴィート]フランス語の「速い」が由来の、高速な開発環境を提供するフロントエンドビルドツールを使ったフロントエンド開発環境(vite.config.ts)では以下のようにプロキシを設定します。

vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://api.external.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
});

この設定により、ブラウザからは http://localhost:3000/api/users への同一オリジンリクエストとして扱われるため、ブラウザのCORSポリシーによるブロックを回避できます。なお、Viteの server.proxy は開発サーバー向けの設定です。本番環境で同様の構成にする場合は、NginxNginx [エンジンエックス]高性能なWebサーバー・リバースプロキシなどのリバースプロキシやバックエンドサーバーを利用して、APIへのリクエストを転送する仕組みを別途構築する必要があります。

[!NOTE] プロキシサーバーやリバースプロキシの詳しい仕組みについては、関連記事の フォワードプロキシとリバースプロキシの違い を参照してください。

まとめ:CORSを正しく理解してトラブルに対処しよう

CORSの重要ポイントをまとめます。

  • **CORS(オリジン間リソース共有)**は、異なるオリジン間の通信において、ブラウザがレスポンスをJavaScriptに公開することを許可するための仕組み。
  • オリジンは「スキーム」「ホスト」「ポート番号」の3要素で決まり、いずれか1つでも異なればクロスオリジンとなる。
  • **同一生成元ポリシー(SOP)**は、異なるオリジンのリソースやレスポンスをJavaScriptから自由に読み取ることを制限するブラウザのセキュリティ機能。
  • 単純リクエストの条件を満たさない場合は、実際のリクエストを送信する前に OPTIONS メソッドによるプリフライトリクエストが実行される。
  • CORSの制御はブラウザ側で行われるため、curl やPostmanなどのブラウザ以外のHTTPクライアントではCORSによる制限を受けない。
  • 認証情報を含むクロスオリジン通信では、Access-Control-Allow-Origin に具体的なオリジンを指定し、Access-Control-Allow-Credentials: true を設定する必要がある。
  • Cookieなどのブラウザ管理の認証情報を含むクロスオリジン通信(credentials modeが include の場合)では、Access-Control-Allow-Origin に具体的なオリジンを指定し、Access-Control-Allow-Credentials: true を設定する必要がある。

CORSエラーに遭遇した際は、ブラウザの開発者ツール(NetworkタブやConsoleタブ)で「リクエストのOrigin」「プリフライトOPTIONSの成否」「サーバーから返ってきたレスポンスヘッダー」を順番に確認し、適切なヘッダー設定やプロキシ設定を行いましょう。


以上で本記事の解説を終わります。
よいITライフを!
ITナレッジライフ 運営者
この記事を書いた人

Z (ITナレッジライフ)

現役のITエンジニア。Linux、プログラミング、IT用語など、日々の業務で得た「痒いところに手が届く」技術情報を発信しています。

プロフィールと編集ポリシーを見る →

ITエンジニアにお勧めの本 ↗

おすすめ

エンジニアが知っておきたい思考の整理術 複雑な情報を【理解する】【伝える】テクニック

難易度
実用性
図解

思考の整理というテーマを、実務ですぐに使える具体的な「技術」として落とし込んだ一冊です。

人気記事


記事を評価

Thanks!
目次
Scroll to Top