cors の使い方 — Node.js CORS ミドルウェア完全ガイド
一言でいうと
cors は、Express/Connect アプリケーションに CORS(Cross-Origin Resource Sharing)レスポンスヘッダーを付与する Node.js ミドルウェアです。ブラウザが異なるオリジンからの API レスポンスを読み取れるかどうかを制御します。
どんな時に使う?
- フロントエンドとバックエンドが別ドメイン/ポートで動いている時 — React(localhost:3000)から Express API(localhost:8080)を呼ぶ開発環境など
- 公開 API を提供する時 — 外部サイトの JavaScript から自社 API を利用可能にしたい場合
- 特定のオリジンだけにアクセスを許可したい時 — 自社のフロントエンドドメインのみ API レスポンスの読み取りを許可する場合
重要: CORS はブラウザが実施するセキュリティ機構です。
corsパッケージはレスポンスヘッダーを設定するだけで、リクエスト自体をブロックするわけではありません。curl や Postman などの非ブラウザクライアントは CORS を無視します。
インストール
# npm
npm install cors
# yarn
yarn add cors
# pnpm
pnpm add cors
TypeScript の型定義も合わせてインストールします:
npm install --save-dev @types/cors
基本的な使い方
最もシンプルなパターンは、すべてのオリジンからのアクセスを許可する方法です。
import express from 'express';
import cors from 'cors';
const app = express();
// すべてのルートで CORS を有効化(Access-Control-Allow-Origin: *)
app.use(cors());
app.get('/api/products/:id', (req, res) => {
res.json({ msg: 'This is CORS-enabled for all origins!' });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
よく使う API・設定パターン
1. 特定のオリジンのみ許可する
import express from 'express';
import cors from 'cors';
const app = express();
const corsOptions: cors.CorsOptions = {
origin: 'https://example.com',
optionsSuccessStatus: 200, // IE11 や一部のスマートTVは 204 で問題が起きる
};
app.use(cors(corsOptions));
// → Access-Control-Allow-Origin: https://example.com
// → Vary: Origin
app.get('/api/data', (req, res) => {
res.json({ data: 'restricted' });
});
app.listen(3000);
2. 複数のオリジンを許可する(配列指定)
import cors from 'cors';
const corsOptions: cors.CorsOptions = {
origin: [
'https://app.example.com',
'https://admin.example.com',
/\.example\.com$/, // example.com のサブドメインすべてにマッチ
],
};
app.use(cors(corsOptions));
3. 動的にオリジンを判定する(DB 連携など)
import cors from 'cors';
const corsOptions: cors.CorsOptions = {
origin: (origin, callback) => {
// origin は undefined になることがある(同一オリジンリクエストや非ブラウザ)
if (!origin) {
return callback(null, true);
}
// 例: DB やキャッシュから許可リストを取得
const allowedOrigins = ['https://app.example.com', 'https://partner.example.com'];
if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
};
app.use(cors(corsOptions));
4. Cookie/認証情報を含むリクエストを許可する
import cors from 'cors';
const corsOptions: cors.CorsOptions = {
origin: 'https://app.example.com', // credentials 使用時は * 不可
credentials: true, // Access-Control-Allow-Credentials: true
};
app.use(cors(corsOptions));
// フロントエンド側: fetch(url, { credentials: 'include' })
5. 特定ルートのみ CORS を有効化 / プリフライト対応
import express from 'express';
import cors from 'cors';
const app = express();
// 全ルートの OPTIONS プリフライトに対応
app.options('*', cors());
// 特定ルートのみ CORS を有効化
app.get('/api/public', cors(), (req, res) => {
res.json({ msg: 'Public endpoint' });
});
// リクエストごとに動的に CORS 設定を切り替え
const dynamicCors: cors.CorsOptionsDelegate = (req, callback) => {
let options: cors.CorsOptions;
if (req.path.startsWith('/auth/')) {
options = { origin: 'https://app.example.com', credentials: true };
} else {
options = { origin: '*' };
}
callback(null, options);
};
app.use(cors(dynamicCors));
app.listen(3000);
設定オプション一覧
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
origin | boolean | string | RegExp | Array | Function | '*' | 許可するオリジン |
methods | string | string[] | 'GET,HEAD,PUT,PATCH,POST,DELETE' | 許可する HTTP メソッド |
allowedHeaders | string | string[] | リクエストの Access-Control-Request-Headers を反映 | 許可するリクエストヘッダー |
exposedHeaders | string | string[] | なし | ブラウザに公開するレスポンスヘッダー |
credentials | boolean | false | Access-Control-Allow-Credentials を設定 |
maxAge | number | なし | プリフライト結果のキャッシュ秒数 |
preflightContinue | boolean | false | プリフライトレスポンスを次のハンドラに渡す |
optionsSuccessStatus | number | 204 | OPTIONS リクエスト成功時のステータスコード |
類似パッケージとの比較
| パッケージ | 特徴 | 週間DL数 | 備考 |
|---|---|---|---|
| cors | Express/Connect 向け定番ミドルウェア | 約1,800万 | 最も広く使われている |
| @fastify/cors | Fastify 専用 CORS プラグイン | 約50万 | Fastify を使うならこちら |
| @koa/cors | Koa 専用 CORS ミドルウェア | 約20万 | Koa を使うならこちら |
| helmet | セキュリティヘッダー全般を設定 | 約200万 | CORS 専用ではないが併用されることが多い |
※ DL 数は目安です。フレームワークに合ったパッケージを選びましょう。
注意点・Tips
1. credentials: true と origin: '*' は併用できない
ブラウザの仕様上、Access-Control-Allow-Credentials: true と Access-Control-Allow-Origin: * の組み合わせはエラーになります。Cookie を使う場合は必ず具体的なオリジンを指定してください。
// ❌ NG: ブラウザがエラーを出す
{ origin: '*', credentials: true }
// ✅ OK: 具体的なオリジンを指定
{ origin: 'https://app.example.com', credentials: true }
2. origin 関数の第一引数は undefined になりうる
同一オリジンからのリクエストや、curl などの非ブラウザクライアントからのリクエストでは origin が undefined になります。関数内で必ずハンドリングしてください。
origin: (origin, callback) => {
// origin が undefined の場合の処理を忘れずに
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
3. app.use(cors()) ならプリフライトは自動対応
アプリケーションレベルで app.use(cors()) を使っている場合、すべてのルートの OPTIONS リクエストが自動的に処理されます。個別ルートに cors() を適用している場合は、app.options() を明示的に追加する必要があります。
4. CORS はセキュリティの「壁」ではない
CORS はブラウザ側の仕組みです。サーバーサイドの認証・認可の代わりにはなりません。API キーや JWT トークンによる認証は別途実装してください。
5. プリフライトのキャッシュで不要なリクエストを減らす
maxAge を設定すると、ブラウザがプリフライトの結果をキャッシュし、毎回の OPTIONS リクエストを省略できます。
const corsOptions: cors.CorsOptions = {
origin: 'https://app.example.com',
maxAge: 86400, // 24時間キャッシュ
};
まとめ
cors は Express/Connect アプリケーションに CORS ヘッダーを設定するためのデファクトスタンダードなミドルウェアです。app.use(cors()) の一行で全オリジン許可から、動的オリジン判定や認証情報付きリクエストまで柔軟に対応できます。ただし、CORS はブラウザ側の仕組みに過ぎないため、サーバーサイドの認証・認可と組み合わせて正しくセキュリティを設計することが重要です。