はじめに

フロントエンドからAPIにアクセスする際に「CORSエラーが出た」「ローカルでは動くのに本番環境では失敗する」といった経験をしたことがある方は多いのではないでしょうか。これらのトラブルの多くは、Webのセキュリティ仕様である「同一オリジンポリシー」や、それを一部緩和する「CORS(Cross-Origin Resource Sharing)」に関係しています。

CORSはよく知られた概念ですが、その仕組みを正確に理解しないまま設定を試行錯誤してしまうケースも少なくありません。この記事では、CORSの背景にあるOriginの考え方から、リクエストの種類、Cookieを扱う際の注意点、開発時の回避方法まで、順を追って丁寧に解説していきます。

Originとは

CORSを理解するための第一歩は、「Origin」という概念を正しく把握することです。Originとは、あるリソースが属する「出どころ」を示す識別子で、次の3要素から構成されます。

  • スキーム(例:httphttps
  • ホスト(例:example.com
  • ポート番号(省略時はスキームによって決まる)

たとえば、以下の2つのURLは見た目は似ていますが、ポート番号が異なるため異なるオリジンと見なされます。

https://example.com:443
https://example.com:8443

この「オリジンが同じかどうか」は、Webブラウザがリソースへのアクセスを許可するかどうかを判断する基準になります。

また、ブラウザがサーバーにリクエストを送信する際には、セキュリティの観点からOriginヘッダーが自動で付与されることがあります。これは「このリクエストはどこから発生したか」をサーバーに知らせるためのものです。

同一オリジンポリシーとは

同一オリジンポリシー(Same-Origin Policy)は、Webブラウザにおける最も基本的なセキュリティモデルのひとつです。このポリシーにより、異なるオリジン間でのリソースアクセスは原則として制限されます。

たとえば、あるWebページが https://example.com 上で動作している場合、JavaScriptから次のようなオリジンに属するリソースにはアクセスできません。

  • http://example.com(スキームが異なる)
  • https://api.example.com(サブドメインが異なる)
  • https://example.com:8443(ポートが異なる)

この制限は、クロスサイトスクリプティング(XSS)やクロスサイトリクエストフォージェリ(CSRF)などの攻撃を防ぐために不可欠です。

とはいえ、実際のWebアプリケーションでは、フロントエンドとバックエンドが別のオリジンに配置されることも珍しくありません。そうしたケースに対応するための仕組みが、**CORS(Cross-Origin Resource Sharing)**です。

CORSとは

CORS(Cross-Origin Resource Sharing)は、Webブラウザが同一オリジンポリシーを一部緩和するための仕組みです。本来ブロックされるはずのクロスオリジンのリクエストに対して、サーバーが明示的に許可を与えることで、安全にリソースを共有できるようにします

この許可は、レスポンスに含まれるAccess-Control-Allow-*系のヘッダーによって制御されます。代表的なヘッダーは以下のとおりです。

  • Access-Control-Allow-Origin:許可するオリジンを指定(例:*https://example.com
  • Access-Control-Allow-Methods:許可するHTTPメソッドを指定
  • Access-Control-Allow-Headers:許可するカスタムヘッダーを指定
  • Access-Control-Allow-Credentials:Cookieなどの資格情報を許可するか(true

CORSでは、ブラウザが送るリクエストの内容と、サーバーのレスポンスヘッダーの組み合わせによって、アクセスの可否が決定されます。

たとえば、https://frontend.com のWebアプリから https://api.backend.com のAPIにアクセスする場合、サーバーがそのオリジンを許可していなければ、ブラウザはレスポンスをブロックします。

単純リクエスト

CORSにおける「単純リクエスト(simple request)」とは、特定の条件を満たすことでプリフライト(事前確認)なしにそのまま送信されるリクエストのことです。

単純リクエストと見なされるには、次のすべての条件を満たす必要があります。

  • HTTPメソッドが GETPOSTHEAD のいずれかである
  • 使用するヘッダーが以下の「安全な」ヘッダーに限られる
    Accept, Accept-Language, Content-Language,
    Content-Type(値が application/x-www-form-urlencodedmultipart/form-datatext/plain のいずれか)
  • ファイルのアップロードやCookieの送信、カスタムヘッダーの使用などがない

このようなリクエストでは、ブラウザは自動的にOriginヘッダーを付加して送信し、サーバーはAccess-Control-Allow-Originなどを用いて応答します。

プリフライトリクエスト

単純リクエストの条件を満たさない場合、ブラウザはリクエストを送信する前に「プリフライトリクエスト(preflight request)」を自動的に行います。これは、サーバーが実際のリクエストを許可するかどうかを事前に確認するための仕組みです。

プリフライトリクエストは、次のような流れで行われます。

  1. ブラウザがOPTIONSメソッドでサーバーに問い合わせる
  2. リクエストに以下のヘッダーを含める
    Origin / Access-Control-Request-Method / Access-Control-Request-Headers
  3. サーバーがCORS用の許可ヘッダーで応答する
  4. 許可された場合に限り、ブラウザが本来のリクエストを送信する

この確認リクエストが適切に処理されないと、本リクエストもブロックされるため、APIの側でも正確なCORS応答を用意することが重要です。

Cookieを使う場合の注意点

CORSを利用してCookieなどの資格情報(credentials)を送信する場合、追加の設定が必要です。

フロントエンドでは、fetchなどのAPIで次のようにcredentialsオプションを明示します。

1
2
3
fetch("https://api.example.com/data", {
  credentials: "include"
});

サーバー側では以下のようにレスポンスヘッダーを設定する必要があります。

  • Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Origin に明示的なオリジンを指定(*は使用不可)

特に注意すべき点として、Access-Control-Allow-Credentials: trueAccess-Control-Allow-Origin: * は併用できません

Node.js + ExpressでのCORS設定例

バックエンドがNode.js + Expressで構成されている場合、CORS設定は非常にシンプルです。公式の cors ミドルウェアを使えば、オリジン制御やCookie対応も容易に行えます。

以下は、CORSを明示的に許可した上で、実用的なGET/POST APIエンドポイントを備えたサンプルです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const express = require("express");
const cors = require("cors");

const app = express();

// CORS設定
app.use(cors({
  origin: "https://example.com", // 許可するオリジン
  credentials: true              // Cookieなどを受け付ける
}));

app.use(express.json()); // JSONボディを扱うためのミドルウェア

app.get("/api/hello", (req, res) => {
  res.json({ message: "Hello from server!" });
});

app.post("/api/data", (req, res) => {
  console.log(req.body);
  res.status(201).json({ status: "received" });
});

// エラーハンドリング(CORS失敗時の参考)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: "Internal server error" });
});

app.listen(3000, () => {
  console.log("Server is running on http://localhost:3000");
});

開発時にCORSを回避する方法

本番ではCORSを正しく設定すべきですが、開発時に素早く検証したい場合はローカルプロキシ特殊なブラウザ起動によって回避できます。

フロントエンド側でのプロキシ設定

React(create-react-app)では、package.jsonに以下を追加するだけで、開発サーバーがAPIへのリクエストを中継してくれます。

1
"proxy": "http://localhost:5000"

これにより、ブラウザ上ではCORSが発生しなくなります。仕組みとしては:

  • ブラウザ → React dev server(localhost:3000) → Nodeサーバー(localhost:5000)

というルートで通信され、オリジンが同一であるかのように見せかけている状態です。

CORS無効モードでブラウザを起動する

セキュリティは無視されますが、Chromeなどを一時的に「CORS無効モード」で起動することもできます。以下はMacの例です。

1
open -na "Google Chrome" --args --disable-web-security --user-data-dir="/tmp/chrome-dev"
  • --disable-web-security:同一オリジンポリシーとCORSを無効化
  • --user-data-dir:隔離されたプロファイルで起動(通常のプロファイルに影響を与えない)

この手法は検証には便利ですが、ログイン情報や個人データを扱わない専用ブラウザに限定してください。

CORSを一時的に無効化するツール

ローカル開発やAPI検証のために、CORSを一時的に無効化するブラウザ拡張を利用するケースもあります。

Chrome拡張「Allow CORS」

「Allow CORS: Access-Control-Allow-Origin」などの拡張を使うと、リクエスト/レスポンスにCORSヘッダーを強制的に挿入することができます。

  • Access-Control-Allow-Origin: * を強制追加
  • プリフライト応答を模倣

ただしこの方法はブラウザ側で無理やり通すものであり、実際のサーバー設定が正しいとは限りません

PostmanやcURLによる代替

PostmanやcURLなど、ブラウザ外で動作するHTTPクライアントは、CORSの影響を一切受けません。そのため:

  • サーバー側のCORS設定が正しいかを確認したい
  • ブラウザ以外の通信手段でもAPIが動作するか確認したい

といった場面では、拡張機能よりもこちらの方法が確実かつ安全です。

まとめ

CORSは、Webブラウザにおけるセキュリティを保ちつつ、必要なクロスオリジン通信を実現するための重要な仕組みです。一見複雑に見えるかもしれませんが、その根底には「どこからのアクセスを許可するか」というシンプルな原則があります。

CORSに関するトラブルの多くは、「何が送られていて、サーバーがどう応答しているか」を理解することで解決できます。ヘッダーを丁寧に確認し、仕組みに即した対応を心がけることが、安定した開発と運用の鍵となります。