Tasuke Hubのロゴ

ITを中心に困っている人を助けるメディア

分かりやすく解決策を提供することで、あなたの困ったをサポート。 全ての人々がスムーズに生活できる世界を目指します。

Node.jsアプリケーションのCORSエラー解決法:5分で実装できる完全ガイド

記事のサムネイル
TH

Tasuke Hub管理人

東証プライム市場上場企業エンジニア

情報系修士卒業後、大手IT企業にてフルスタックエンジニアとして活躍。 Webアプリケーション開発からクラウドインフラ構築まで幅広い技術に精通し、 複数のプロジェクトでリードエンジニアを担当。 技術ブログやオープンソースへの貢献を通じて、日本のIT技術コミュニティに積極的に関わっている。

🎓情報系修士🏢東証プライム上場企業💻フルスタックエンジニア📝技術ブログ執筆者

CORSエラーとは?開発者が直面する一般的な問題

フロントエンドアプリケーションから異なるドメインのAPIにリクエストを送ると、ブラウザのコンソールに次のようなエラーが表示されることがあります。

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

これが「Cross-Origin Resource Sharing (CORS)」エラーです。ブラウザのセキュリティ機能によって、異なるオリジン(ドメイン、ポート、プロトコル)間でのリソース共有が制限されています。

CORSエラーが発生する主な状況は以下のとおりです:

  • ローカル開発環境(localhost:3000)からAPIサーバー(localhost:8000)へのリクエスト
  • フロントエンドとバックエンドが異なるドメインで稼働している場合
  • 開発環境と本番環境での設定の違いによるエラー

このエラーは単なる設定の問題であり、適切なヘッダーを設定することで簡単に解決できます。Node.jsとExpress.jsを使う場合、正しいCORS設定を実装して、フロントエンドからのリクエストを許可する方法を見ていきましょう。

Express.jsでCORS対応を実装する方法

Express.jsでCORS対応を実装する最も簡単な方法は、corsミドルウェアを使用することです。以下の手順で簡単に実装できます。

1. cors ミドルウェアのインストール

npm install cors
# または
yarn add cors

2. ミドルウェアの設定

const express = require('express');
const cors = require('cors');
const app = express();

// すべてのルートで CORS を有効にする
app.use(cors());

// APIルートの設定
app.get('/api/data', (req, res) => {
  res.json({ message: 'This is your data!' });
});

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

これだけで、あらゆるオリジンからのリクエストを許可する基本的なCORS設定が完了します。ブラウザはレスポンスに含まれる以下のようなヘッダーを確認します:

Access-Control-Allow-Origin: *

この設定はシンプルですが、本番環境ではセキュリティ上の理由から、より制限的な設定が推奨されます。次のセクションでは、より詳細なカスタマイズ方法を見ていきましょう。

特定のオリジンやヘッダーに対するCORS設定のカスタマイズ

実際の開発では、特定のオリジンからのリクエストのみを許可したり、特定のHTTPヘッダーやメソッドを許可したりする必要があります。corsミドルウェアではこれらの設定を簡単にカスタマイズできます。

特定のオリジンのみを許可する

const corsOptions = {
  origin: 'https://myapp.com',  // 特定のドメインのみ許可
  optionsSuccessStatus: 200 // レガシーブラウザ対応
};

app.use(cors(corsOptions));

複数のオリジンを許可する

const corsOptions = {
  origin: ['https://myapp.com', 'https://admin.myapp.com'],
  optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

関数を使用して動的に判断する

const corsOptions = {
  origin: function (origin, callback) {
    // ホワイトリストの定義
    const whitelist = ['https://myapp.com', 'https://admin.myapp.com'];
    
    // 開発環境では undefined になることがあるため許可
    if (!origin || whitelist.indexOf(origin) !== -1) {
      callback(null, true);
    } else {
      callback(new Error('CORS policy violation'));
    }
  },
  optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

その他のカスタマイズオプション

const corsOptions = {
  origin: 'https://myapp.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE'], // 許可するHTTPメソッド
  allowedHeaders: ['Content-Type', 'Authorization'], // 許可するHTTPヘッダー
  exposedHeaders: ['Content-Range', 'X-Total-Count'], // クライアントに公開するヘッダー
  credentials: true, // Cookieを含めたリクエストを許可
  maxAge: 86400 // プリフライトリクエストの結果をキャッシュする時間(秒)
};

app.use(cors(corsOptions));

これらの設定を使用して、アプリケーションのセキュリティ要件に合わせたCORS設定を実装できます。

フロントエンドからのプリフライトリクエストを処理する

ブラウザは、特定の「複雑な」リクエストを送信する前に、サーバーがそのリクエストを受け入れるかどうかを確認するために「プリフライトリクエスト」を送信します。これは OPTIONS メソッドを使用して行われます。

プリフライトリクエストが発生する主な条件は以下のとおりです:

  1. PUTDELETECONNECTOPTIONSTRACEPATCH メソッドを使用する場合
  2. Content-Type が application/json などの単純でないタイプの場合
  3. カスタムヘッダーを含む場合(例:X-API-Key

プリフライトリクエストへの対応

corsミドルウェアは通常、プリフライトリクエストを自動的に処理しますが、より細かい制御が必要な場合は以下のように実装できます:

// OPTIONS リクエストに対する特別な処理
app.options('/api/data', cors(corsOptions)); // プリフライト用の特定ルート

// 実際のAPIエンドポイント
app.put('/api/data', cors(corsOptions), (req, res) => {
  // リソースを更新するコード
  res.json({ success: true });
});

プリフライトリクエストの例

ブラウザからのプリフライトリクエストは次のようになります:

OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization

サーバーからの適切なレスポンスは:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

このレスポンスにより、ブラウザは実際のPUTリクエストを送信することが許可されます。Access-Control-Max-Age ヘッダーは、ブラウザがこのプリフライトレスポンスをキャッシュする時間(秒)を指定します。

開発環境と本番環境でのCORS設定の違い

開発環境と本番環境では、異なるCORS設定が必要になることがよくあります。これらの環境間で設定を切り替える方法を見ていきましょう。

環境変数を使用した設定

Node.jsでは、環境変数を使用して異なる環境での設定を管理するのが一般的です。

// .env.development と .env.production に環境変数を定義

// app.js
const express = require('express');
const cors = require('cors');
const app = express();

// 環境に応じてCORS設定を変更
const corsOptions = {
  origin: process.env.NODE_ENV === 'production'
    ? process.env.ALLOWED_ORIGIN // 例: 'https://myapp.com'
    : '*', // 開発環境ではすべてのオリジンを許可
  credentials: true
};

app.use(cors(corsOptions));

複数の環境に対応する動的な設定

const express = require('express');
const cors = require('cors');
const app = express();

// 環境に応じたオリジンリストの設定
const whitelist = {
  development: ['http://localhost:3000', 'http://localhost:8080'],
  test: ['http://test.myapp.com'],
  production: ['https://myapp.com', 'https://admin.myapp.com']
};

const corsOptions = {
  origin: function (origin, callback) {
    const env = process.env.NODE_ENV || 'development';
    const allowedOrigins = whitelist[env];
    
    // 許可されたオリジンリストにあるか、
    // 開発環境でのnull origin(例:Postmanからのリクエスト)を許可
    if (!origin || allowedOrigins.indexOf(origin) !== -1) {
      callback(null, true);
    } else {
      callback(new Error(`CORS policy violation: ${origin} is not allowed`));
    }
  },
  credentials: true
};

app.use(cors(corsOptions));

環境固有のミドルウェア設定

const express = require('express');
const cors = require('cors');
const app = express();

// 本番環境用のCORS設定
const productionCors = cors({
  origin: 'https://myapp.com',
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  maxAge: 86400
});

// 開発環境用のCORS設定
const developmentCors = cors({
  origin: '*',
  credentials: true
});

// 環境に応じたミドルウェアの選択
if (process.env.NODE_ENV === 'production') {
  app.use(productionCors);
} else {
  app.use(developmentCors);
}

これらの方法を使用して、開発中は柔軟なCORS設定を使用し、本番環境ではより制限的な設定を適用できます。

トラブルシューティング:よくあるCORSエラーとその解決策

CORSエラーはさまざまな形で発生します。ここでは一般的なエラーパターンとその解決策を紹介します。

1. 「No 'Access-Control-Allow-Origin' header is present」エラー

エラーメッセージ:

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

解決策:

  • サーバー側でCORSミドルウェアが正しく設定されているか確認する
  • 正しいオリジンが許可リストに含まれているか確認する
// 基本的なCORS設定を実装
app.use(cors({
  origin: 'https://myapp.com' // リクエスト元のドメインを指定
}));

2. 「Method not allowed by CORS」エラー

エラーメッセージ:

Access to fetch at 'https://api.example.com/data' from origin 'https://myapp.com' 
has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods 
in preflight response.

解決策:

  • 許可するメソッドをCORS設定に追加する
app.use(cors({
  origin: 'https://myapp.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE'] // PUTメソッドを追加
}));

3. 「Header not allowed by CORS」エラー

エラーメッセージ:

Access to fetch at 'https://api.example.com/data' from origin 'https://myapp.com' 
has been blocked by CORS policy: Request header field x-custom-header is not allowed 
by Access-Control-Allow-Headers in preflight response.

解決策:

  • 許可するヘッダーをCORS設定に追加する
app.use(cors({
  origin: 'https://myapp.com',
  allowedHeaders: ['Content-Type', 'Authorization', 'x-custom-header']
}));

4. 「Credentials flag is true, but Access-Control-Allow-Credentials is not 'true'」エラー

エラーメッセージ:

Access to fetch at 'https://api.example.com/data' from origin 'https://myapp.com' 
has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' 
header in the response is '' which must be 'true' when the request's credentials mode is 'include'.

解決策:

  • credentialsオプションを有効にする
// サーバー側
app.use(cors({
  origin: 'https://myapp.com',
  credentials: true
}));

// クライアント側(フロントエンド)
fetch('https://api.example.com/data', {
  credentials: 'include' // クッキーなどの認証情報を含める
});

5. Node.js以外のサーバーを使用している場合

Express.jsを使用していない場合でも、適切なヘッダーを設定することでCORSを有効にできます:

// Node.js(expressなし)の例
const http = require('http');

http.createServer((req, res) => {
  // CORSヘッダーを設定
  res.setHeader('Access-Control-Allow-Origin', 'https://myapp.com');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  
  // プリフライトリクエスト(OPTIONS)の処理
  if (req.method === 'OPTIONS') {
    res.writeHead(204); // No Content
    res.end();
    return;
  }
  
  // 実際のリクエスト処理
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ message: 'This is your data!' }));
}).listen(8000);

6. プロキシを使用する方法

フロントエンド開発で一時的にCORSエラーを回避するには、開発サーバーのプロキシ機能を使用する方法もあります:

Viteの例:

// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true
      }
    }
  }
};

Create React Appの例:

// package.json
{
  "proxy": "http://localhost:8000"
}

最終的には本番環境では適切なCORS設定を実装する必要がありますが、開発環境でのテストには便利な手法です。

CORSの設定は一見複雑に見えますが、基本的な原則を理解し、適切なミドルウェアを使用することで、簡単に解決できる問題です。ブラウザとサーバー間の安全な通信を確保するための重要なセキュリティ機能であることを忘れないようにしましょう。

このトピックはこちらの書籍で勉強するのがおすすめ!

この記事の内容をさらに深く理解したい方におすすめの一冊です。実践的な知識を身につけたい方は、ぜひチェックしてみてください!

おすすめ記事

おすすめコンテンツ