はじめに:なぜCORSは必要なのか?

Web開発を行っていると、赤い文字で表示される「CORSエラー」に遭遇した経験は誰にでもあるでしょう。これは単なるエラーではなく、Webのセキュリティを支える重要な仕組み「同一オリジンポリシー(Same-Origin Policy)」に基づいています。このポリシーは、あるオリジン(プロトコル、ホスト、ポートの組み合わせ)から読み込まれた文書やスクリプトが、他のオリジンとやり取りすることを制限するものです。

しかし、現代のWebアプリケーションでは、APIサーバーとフロントエンドでドメインが異なるなど、異なるオリジン間でリソースを共有する必要があります。この制約を安全に緩和するための仕組みが「オリジン間リソース共有(Cross-Origin Resource Sharing)」、すなわちCORSです。この記事では、CORSの仕組みから正しい設定方法、そしてよくあるエラーの解決策までを具体的に解説していきます。

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

CORSにおける通信は、大きく分けて「単純リクエスト」と「プリフライトリクエスト」の2種類があります。ブラウザはリクエストの種類に応じて自動的に挙動を切り替えます。

単純リクエスト (Simple Request)は、以下の条件をすべて満たすリクエストです。

  • メソッドがGET, HEAD, POSTのいずれか
  • 手動で設定できるヘッダーが特定の少数(Accept, Accept-Language, Content-Languageなど)に限定される
  • Content-Typeヘッダーが特定の値(application/x-www-form-urlencoded, multipart/form-data, text/plain)のいずれか

この場合、ブラウザはリクエストヘッダーにOriginを付与して直接サーバーにリクエストを送信します。サーバーがレスポンスヘッダーにAccess-Control-Allow-Originを含めて応答すれば、ブラウザは通信を許可します。

プリフライトリクエスト (Preflight Request)は、単純リクエストの条件に当てはまらない場合(例:PUTやDELETEメソッド、Content-Type: application/json、カスタムヘッダーなど)に行われます。実際のリクエストの前に、OPTIONSメソッドによる「事前問い合わせ」がサーバーに送信されます。このリクエストにはAccess-Control-Request-MethodやAccess-Control-Request-Headersといったヘッダーが含まれ、サーバーがこれらを許可するかどうかを問い合わせます。サーバーが許可する旨のヘッダーを返した場合にのみ、ブラウザは実際のリクエストを送信します。

サーバーサイドでのCORS設定:具体的な実装例 (Node.js/Express)

CORSエラーはクライアントサイドではなく、サーバーサイドの設定で解決する問題です。ここでは、Node.jsのフレームワークであるExpressを例に、CORSを設定する方法を見ていきましょう。一般的にはcorsというミドルウェアパッケージを使用するのが簡単です。

まず、必要なパッケージをインストールします。

npm install express cors

次に、Expressアプリケーションでcorsミドルウェアを使用します。最も簡単な設定は、すべてのオリジンからのリクエストを許可する方法ですが、これはセキュリティリスクがあるため主に開発環境で使用します。

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

// すべてのオリジンからのリクエストを許可
app.use(cors());

app.get('/api/data', (req, res) => {
  res.json({ message: 'This is CORS-enabled for all origins!' });
});

app.listen(3001, () => console.log('Server running on port 3001'));

本番環境では、許可するオリジンを明示的に指定することが強く推奨されます。以下のようにオプションを設定します。

const corsOptions = {
  origin: 'https://your-frontend-domain.com',
  methods: ['GET', 'POST'], // 許可するメソッドを指定
  allowedHeaders: ['Content-Type', 'Authorization'], // 許可するヘッダーを指定
  optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

このように、originプロパティにフロントエンドアプリケーションのドメインを指定することで、意図しないドメインからのアクセスをブロックできます。

よくあるCORSエラーと解決策

開発中に遭遇する典型的なCORSエラーメッセージと、その原因および解決策をいくつか紹介します。エラーを解決する際は、ブラウザの開発者ツールの「ネットワーク」タブで、問題のリクエスト(特にOPTIONSリクエスト)のヘッダーを確認するのが基本です。

エラー1: No 'Access-Control-Allow-Origin' header is present on the requested resource.

  • 原因: サーバーからのレスポンスにAccess-Control-Allow-Originヘッダーが含まれていないか、含まれている値がリクエスト元のオリジンと一致しません。
  • 解決策: サーバー側のCORS設定で、リクエスト元のオリジンを許可するようにoriginオプションを正しく設定します。ワイルドカード(*)を使う場合は、資格情報(Cookieなど)を伴うリクエストでは動作しない点に注意が必要です。

エラー2: Request header field 'X-Custom-Header' is not allowed by Access-Control-Allow-Headers in preflight response.

  • 原因: リクエストにカスタムヘッダー(例: X-Custom-Header)を含めていますが、プリフライトリクエストに対するサーバーの応答に、そのヘッダーを許可するAccess-Control-Allow-Headersが含まれていません。
  • 解決策: サーバー側のCORS設定で、allowedHeadersオプションに使用するカスタムヘッダーを追加します。

エラー3: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

  • 原因: PUTメソッドでリクエストを送信しようとしましたが、サーバーがAccess-Control-Allow-MethodsヘッダーでPUTを許可していません。
  • 解決策: サーバー側のCORS設定で、methodsオプションに'PUT'を追加します。

まとめ:CORSは敵ではなく味方

CORSエラーは開発者を悩ませることがありますが、それはWebのセキュリティを守るための重要な防衛機能です。CORSは「エラー」ではなく、ブラウザが「危険な可能性のあるリクエストをブロックしました」と教えてくれる「警告」と捉えるのが正しいでしょう。

重要なポイントは以下の通りです。

  • CORSは、同一オリジンポリシーの制約を安全に緩和するための仕組みである。
  • リクエストは「単純リクエスト」と「プリフライトリクエスト」に大別され、挙動が異なる。
  • CORSの問題は、サーバーサイドで適切なAccess-Control-*ヘッダーを返すことで解決する。
  • 本番環境では、originを安易に*(ワイルドカード)にせず、許可するドメインを明示的に指定することがセキュリティ上不可欠。

この基本を理解すれば、CORSエラーに遭遇しても冷静に対処できるようになります。CORSを正しく設定し、安全でモダンなWebアプリケーションを構築していきましょう。