7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CORSを一緒に理解しよう(Cross-Origin Resource Sharing)

7
Posted at

1. はじめに

フロントエンドが localhost:3000、バックエンドが localhost:8080 で動いているアプリを作っているとします。一見問題なさそうに見えても、コンソールを開くとこんなエラーが表示されることがあります:

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

これがCORSエラーです — Web開発において最もよく遭遇するエラーの一つです。この記事では、CORSが何なのか、なぜ存在するのか、そして正しい対処法について解説します。

Cross-Origin Resource Sharing(CORS) とは、サーバーがブラウザに対して、どのオリジンからのリソースアクセスを許可するかを指定できるHTTPベースの仕組みです。

CORSは、ブラウザのセキュリティの基盤である Same-Origin Policy(SOP) を拡張するために設計されており、SOPはデフォルトで異なるオリジン間のリクエストをブロックします。

実際の開発では、フロントエンドとバックエンドが異なるドメインやポートにデプロイされている場合によく発生します。


2. 基本概念

2.1 オリジンとは?

オリジンは以下の3つの要素で構成されます:スキーム + ホスト + ポート

URL オリジン
https://example.com https + example.com + 443
http://example.com http + example.com + 80
https://example.com:3000 https + example.com + 3000

2つのURLが「同じオリジン」と見なされるのは、上記3要素がすべて一致している場合のみです。

2.2 Same-Origin Policy

Same-Origin Policyはブラウザのセキュリティ機能です:

  • リクエストの送信は許可する
  • ただし、オリジンが異なる場合はレスポンスの読み取りをブロックする

これにより、あるWebページが制御なしに別のオリジンのデータにアクセスすることを防いでいます。

重要: リクエスト自体はサーバーに届きます。ブラウザが判断するのは、JavaScriptがそのレスポンスを読み取れるかどうかです。

2.3 なぜCORSが必要なのか?

Same-Origin Policyはセキュリティ向上に役立ちますが、正当なユースケースでも制限が生じることがあります:

  • フロントエンドとバックエンドが異なるドメインで動作している
  • サードパーティAPIを利用している
  • マイクロサービスアーキテクチャを採用している

CORSは、SOPのデフォルト動作(全ブロック)の代わりに、サーバーが許可するオリジンを明示的に指定できるようにするために導入されました。


3. CORSの仕組み

CORSはHTTPヘッダーを使用して、異なるオリジンからのリソースアクセスを許可または拒否します。

3.1 基本的な流れ

  1. ブラウザがヘッダー付きでリクエストを送信:
Origin: https://example.com
  1. サーバーがレスポンスを返す:
Access-Control-Allow-Origin: https://example.com
  1. ブラウザが確認する:
    • オリジンが有効 → レスポンスの読み取りを許可
    • 無効 → レスポンスをブロック

この確認処理はすべてブラウザ側で行われます。


4. CORSにおけるリクエストの種類

すべてのリクエストが同じように処理されるわけではありません。

4.1 シンプルリクエスト

以下の条件を満たすリクエストは「シンプルリクエスト」と見なされます:

  • メソッド: GET、POST、HEAD
  • カスタムヘッダーなし
  • Content-Type が以下のいずれか:application/x-www-form-urlencodedmultipart/form-datatext/plain

➡️ シンプルリクエストの場合:ブラウザは直接リクエストを送信し、プリフライトは発生しません。

4.2 プリフライトリクエスト

シンプルリクエストの条件を満たさない場合、ブラウザはまず OPTIONSリクエスト を送信します:

OPTIONS /api/data
Origin: https://example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type

サーバーのレスポンス:

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: Content-Type

レスポンスが有効であれば、ブラウザは本来のリクエストを送信します。

開発中に OPTIONS リクエストを処理していないことが、CORSエラーのよくある原因です。

4.3 CORSで使用されるヘッダー

リクエストヘッダー(ブラウザが送信):

ヘッダー 説明
Origin リクエストのオリジンを示す
Access-Control-Request-Method 本リクエストのメソッド(プリフライト時)
Access-Control-Request-Headers 送信予定のカスタムヘッダー

レスポンスヘッダー(サーバーが返す):

ヘッダー 説明
Access-Control-Allow-Origin アクセスを許可するオリジン
Access-Control-Allow-Methods 許可するメソッド
Access-Control-Allow-Headers 許可するヘッダー
Access-Control-Allow-Credentials Cookieや認証情報の送信を許可する
Access-Control-Max-Age プリフライトのキャッシュ時間

5. CredentialsとCORS

Cookie や Authorization ヘッダーなどの認証情報を含むリクエストの場合:

fetch("https://api.example.com", {
  credentials: "include"
})

サーバー側で以下の設定が必要です:

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true

注意: Allow-Credentials: true の場合、* は使用できません。オリジンを具体的に指定する必要があります。


6. 実践例

基本的な例

リクエスト:

GET /api/user
Origin: https://frontend.com

レスポンス:

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

ブラウザはJavaScriptがレスポンスデータを読み取ることを許可します。

サーバー側の設定例

Node.js(Express):

const cors = require('cors');

app.use(cors({
  origin: ['https://yourdomain.com', 'https://app.yourdomain.com'],
  methods: ['GET', 'POST', 'PUT'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
}));

Nginx:

location /api/ {
    add_header 'Access-Control-Allow-Origin' 'https://yourdomain.com';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';

    if ($request_method = OPTIONS) {
        add_header 'Access-Control-Max-Age' 86400;
        return 204;
    }
}

7. よくある誤解

7.1 「CORSはサーバーを保護する」

CORSはリクエストがサーバーに届くことを防ぎません。ブラウザのJavaScriptがレスポンスを読み取れないようにするだけです。

7.2 「バックエンドの設定だけで十分」

CORSはブラウザが実施する仕組みです。curl や Postman などのツールには影響しません。

7.3 「* を使っても安全」

* はパブリックAPIには適切な場合もありますが、機密データを扱うAPIには使用すべきではありません。


8. ベストプラクティス

8.1 オリジンを具体的に指定する

Access-Control-Allow-Origin: https://yourdomain.com

8.2 メソッドとヘッダーを制限する

Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type

8.3 null をホワイトリストに入れない

null オリジンは file:// やサンドボックス化されたiframeから発生する可能性があり、セキュリティリスクになります。

8.4 プリフライトをキャッシュする

Access-Control-Max-Age: 86400

プリフライトリクエストはアプリケーションデータを含みませんが、ネットワークとサーバーのリソースを消費します。キャッシュすることでOPTIONSリクエストの数を減らせます。

8.5 環境ごとに設定を変える

const allowedOrigins = process.env.NODE_ENV === 'production'
  ? ['https://yourdomain.com']
  : ['http://localhost:3000', 'http://localhost:8000'];

9. CORSのデバッグ

CORSエラーが発生した場合、以下のパターンのいずれかに該当することが多いです:

よくあるエラーメッセージ:

// エラー1: サーバーがCORSヘッダーを返していない
Access to fetch at '...' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

// エラー2: オリジンが許可されていない
The 'Access-Control-Allow-Origin' header has a value 'https://other.com'
that is not equal to the supplied origin.

// エラー3: プリフライトが失敗
Response to preflight request doesn't pass access control check.

デバッグチェックリスト:

  1. リクエストの Origin ヘッダーを確認する(DevToolsのNetworkタブ)
  2. レスポンスに Access-Control-Allow-Origin が含まれているか確認する
  3. プリフライトがある場合:OPTIONS リクエストが 200/204 を返しているか?
  4. credentialsを使用している場合:Allow-Credentials: true になっており、オリジンが * ではなく具体的に指定されているか?

多くの場合、リクエスト自体は正常に届いていますが、ブラウザ側でレスポンスの読み取りがブロックされています。NetworkタブでOPTIONSリクエストも含めて詳しく確認しましょう。


10. 関連するセキュリティメカニズム

CORSは単独で動作するわけではありません:

  • Same-Origin Policy:CORSが拡張する基盤となるポリシー
  • CSRF:SOPとCORSはCSRFのリスクを軽減しますが、CSRFトークンの代替にはなりません
  • Content Security Policy(CSP):読み込むリソースのソースを制御し、CORSを補完します
  • Cookie/認証:CORSはレスポンスを読めるオリジンを制御しますが、サーバー側での独立した認証・認可も引き続き必要です

11. CORSでできないこと

CORSは包括的なセキュリティ機構ではありません。ブラウザが異なるオリジンからのレスポンスをJavaScriptに読み取らせるかどうかを制御するだけです。

以下のことはできません

  • サーバーへのリクエスト送信を防ぐ
  • JavaScriptの実行を制御する
  • XSSなどの攻撃から保護する
  • 認証・認可の代替にはならない — サーバーはtoken、sessionなどで独立してアクセス権を検証する必要があります

そのため、CORSはCSPなど他のセキュリティメカニズムと組み合わせて使用されるのが一般的です。


12. 参考資料


13. まとめ

CORSはブラウザセキュリティにおいて重要な仕組みです:

  • Same-Origin Policyを拡張する
  • HTTPヘッダーをベースにしている
  • サーバーではなくブラウザが実施する
    CORSを正しく理解することで、エラーの効率的なデバッグ、正確なシステム設定、そしてよくある誤解の回避につながります。特に重要なのは、CORSは完全なセキュリティの壁ではなく、全体的なセキュリティ対策の一層に過ぎないという点です。
7
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?