---
title: "CORSとは？仕組み・プリフライト・サーバー設定例・エラー解決を実装目線で解説"
url: "https://www.issoh.co.jp/tech/details/13763/"
published: 2026-07-13
updated: 2026-09-07
categories: ["セキュリティ"]
publisher: "株式会社一創"
---

# CORSとは？仕組み・プリフライト・サーバー設定例・エラー解決を実装目線で解説

CORS（Cross-Origin Resource Sharing／オリジン間リソース共有）は、ブラウザ上のJavaScriptが自分と異なるオリジンのAPIを呼び出すとき、そのアクセスをサーバー側が許可しているかを確認する仕組みです。フロントエンドとAPIを別ドメインで運用すると、開発中に「Blocked by CORS policy」というエラーで通信が止まります。この記事では、同一オリジンポリシーとの関係、プリフライトリクエストの流れ、Nginx・Apache・Express・Spring Bootでの設定例、curlでプリフライトを再現して返り値を確かめる手順、そしてエラーの原因切り分けと解決までを実装者の視点でまとめました。仕様の根拠はWHATWGのFetch標準とMDNの記述に当たり、設定例はそのままコピーして動かせる形で置いています。

## まとめ：CORS設定はサーバー側・エラー原因はレスポンスヘッダー

CORSはブラウザだけで完結する仕組みではありません。制限をかけるのはブラウザですが、それを緩めて通信を許可するのはサーバー側です。フロントエンドのコードをいくら直してもCORSエラーは消えません。解決の起点は常にAPIサーバーのレスポンスヘッダーにあります。

実装で押さえる順序は4つです。第一に、リクエストがシンプルリクエストかプリフライトを伴うかを見分ける。第二に、サーバーが返すべき`Access-Control-Allow-Origin`などのヘッダーを、使うメソッド・ヘッダー・認証情報に合わせて設定する。第三に、ブラウザに頼らずcurlでOPTIONSを送り、返るヘッダーを目で確かめる。第四に、エラーメッセージの文言から不足しているヘッダーを逆算する。認証付きリクエストではオリジンにワイルドカード`*`を使えないなど、単純設定では踏み抜く落とし穴もあわせて示します。仕様の一次情報は[WHATWGのFetch標準](https://fetch.spec.whatwg.org/)にまとまっており、ヘッダーごとの挙動は[MDNのCORSガイド](https://developer.mozilla.org/ja/docs/Web/HTTP/Guides/CORS)で確認できます。

## CORSの仕組みと同一オリジンポリシーによるアクセス制限の関係

CORSを理解する起点は、ブラウザが持つ同一オリジンポリシー（Same-Origin Policy）です。この制約があるからこそ、CORSという緩和の仕組みが必要になります。制約そのものの成り立ちは[同一オリジンポリシーの基本的な仕組みと役割](https://www.issoh.co.jp/tech/details/7544/)で整理しているので、前提から確認したい場合はそちらを先に読むと本記事の設定作業が追いやすくなります。

### オリジンを構成するスキーム・ホスト・ポートによる一致判定の基準

オリジンは「スキーム（プロトコル）」「ホスト（ドメイン）」「ポート番号」の3つで決まります。この3つがすべて一致して初めて同一オリジンです。1つでも違えば別オリジン扱いになり、CORSの対象になります。

| 比較対象URL                  | 基準オリジンとの関係 | 理由              |
| ------------------------ | ---------- | --------------- |
| https://example.com/api  | 同一オリジン     | スキーム・ホスト・ポートが一致 |
| http://example.com       | 別オリジン      | スキームがhttpで異なる   |
| https://api.example.com  | 別オリジン      | ホストが異なる         |
| https://example.com:8080 | 別オリジン      | ポート番号が異なる       |

実務で多い誤解は「同じドメインなら大丈夫」というものです。`www.example.com`と`api.example.com`はサブドメインが違うため別オリジンで、CORS設定が要ります。ローカル開発でフロントを`localhost:3000`、APIを`localhost:8000`で動かす構成も、ポートが違うため別オリジンです。なおパスやクエリ文字列はオリジンの一致判定に含まれません。`https://example.com/api/v1`と`https://example.com/admin`は同一オリジンとして扱われるので、パス単位で許可を分けたい場合はCORSではなくサーバー側の認可で制御します。

### ブラウザが別オリジンのレスポンス読み取りを制限する本当の理由

同一オリジンポリシーは、悪意あるサイトが利用者のログインセッションを使って別サービスのAPIを勝手に叩き、その応答を読み取ることを防ぎます。もしこの制約が無ければ、利用者が閲覧中の攻撃サイトから、別タブで開いている社内システムやメールのデータを盗み読みできてしまいます。CORSは「安全な範囲でこの制約を緩める」ための正式な手続きであり、制約を無効化する裏技ではありません。`Access-Control-Allow-Origin`で許可するオリジンを絞るのは、この安全性を保つためです。ブラウザ側でスクリプトの実行元を制限する[コンテンツセキュリティポリシー（CSP）](https://www.issoh.co.jp/tech/details/16358/)とは目的が別で、CSPが「何を読み込ませるか」を絞るのに対し、CORSは「どのオリジンにレスポンスを読ませるか」を絞ります。両者は併用する前提の別レイヤーです。

### レスポンスの読み取りだけを止めリクエスト送信は止めない仕組み

ここは実装者が混乱しやすい点です。CORSはリクエストの送信そのものを止めるとは限りません。単純なリクエストの場合、サーバーには実際にリクエストが届き、処理も実行されます。ブラウザが止めるのは「返ってきたレスポンスをJavaScriptに渡すこと」です。つまりGETやPOSTがサーバー側に到達し、データ更新まで走った後で、フロント側だけがエラーになる状況が起こり得ます。副作用のある操作をGETで実装しない、といった設計上の配慮に関係する論点です。この「送信は通るが読み取りは止まる」という性質はCSRFが成立する土台でもあるため、対策の考え方は[XSSとCSRFの違いと実装レベルの対策](https://www.issoh.co.jp/tech/details/4109/)とあわせて押さえておくと設計の抜けが減ります。

## プリフライトリクエストとシンプルリクエストを見分ける判定条件

CORSには2つの経路があります。プリフライトを伴う経路と伴わない経路です。どちらになるかは、送るリクエストの中身によって自動的に決まります。

### ブラウザがプリフライトを伴わないシンプルリクエストと判定する条件

次の条件をすべて満たすと、ブラウザは事前確認なしでリクエストを送ります（[MDNのCORSガイド](https://developer.mozilla.org/ja/docs/Web/HTTP/Guides/CORS)および[Fetch標準](https://fetch.spec.whatwg.org/)のCORS-safelisted request-headerの定義、2026年9月時点）。

- メソッドが`GET`・`HEAD`・`POST`のいずれか
- 手動で付けるヘッダーが`Accept`・`Accept-Language`・`Content-Language`・`Content-Type`など限られたものだけ
- `Content-Type`が`application/x-www-form-urlencoded`・`multipart/form-data`・`text/plain`のいずれか
- ヘッダー値の長さやアップロード側のイベントリスナー登録など、Fetch標準が定める付随条件も外れていない

逆に言えば、JSON APIでよく使う`Content-Type: application/json`を付けた時点でシンプルリクエストの条件を外れ、プリフライトが発生します。認証トークンを`Authorization`ヘッダーで送る場合も同じです。[Bearer TokenをAuthorizationヘッダーで送る実装](https://www.issoh.co.jp/tech/details/4150/)を採るなら、その時点でプリフライト前提の設計になると考えてください。現代のSPAとJSON APIの組み合わせは、ほぼプリフライトを伴います。

### プリフライトで先に飛ぶOPTIONSリクエストの中身と確認内容

プリフライトでは、本来のリクエストの前に`OPTIONS`メソッドの問い合わせが自動で飛びます。ブラウザは「これからこのメソッド・このヘッダーで送りたいが許可されるか」をサーバーに確認します。

1. ブラウザが`OPTIONS /api/users`を送り、`Access-Control-Request-Method`（実行したいメソッド）と`Access-Control-Request-Headers`（付けたいヘッダー）を添える
2. サーバーが`Access-Control-Allow-Methods`・`Access-Control-Allow-Headers`・`Access-Control-Allow-Origin`を返して許可範囲を答える
3. 許可範囲に収まっていれば、ブラウザが本来のリクエスト（PUTやDELETEなど）を送る

この仕組みのため、CORS対応では`OPTIONS`への応答を必ず用意する必要があります。本来のGETやPOSTだけ設定してOPTIONSを404や405で返す実装は、プリフライトの段階で失敗します。プリフライトのOPTIONSには認証情報が乗らない点も見落としがちで、認証必須のミドルウェアをOPTIONSにも噛ませていると401が返り、本リクエストまで到達しません。認証チェックの手前でOPTIONSを抜ける経路を用意します。

### Access-Control-Max-Ageでプリフライト回数を抑える設定

プリフライトはリクエストごとに往復が1回増えるため、呼び出しが多いAPIでは体感速度に響きます。サーバーが`Access-Control-Max-Age`を返すと、ブラウザはその秒数だけプリフライト結果をキャッシュし、同じ条件の再確認を省きます。[MDNのAccess-Control-Max-Ageの記述](https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Headers/Access-Control-Max-Age)によると、ヘッダーを指定しない場合の既定値は5秒、キャッシュできる上限はFirefoxが24時間（86400秒）、Chromium系はv76以降が2時間（7200秒）で、それ以前は10分（600秒）でした（2026年9月時点の記載）。上限を超える値を設定してもブラウザ側で丸められるため、7200秒より大きい指定はChromium系では意味を持ちません。まず600秒程度から始め、設定変更の反映を確認しながら伸ばす運用が無難です。

## サーバー別にCORSヘッダーを設定して動かすまでの実装手順と集約方針

ここが競合記事の手薄な部分であり、実装者が最も知りたい箇所です。CORSヘッダーはリバースプロキシ・Webサーバー・アプリケーションのどの層でも設定できます。層を分けて管理すると設定の重複や矛盾が起きるため、原則としてどこか1か所に集約します。以下の設定例はいずれもそのまま貼り付けて動かせる形にしてあるので、許可オリジンと上流ホスト名だけ自分の環境に置き換えてください。

### Nginxでリバースプロキシ層にCORSヘッダーを付与する設定例

APIの前段にNginxを置く構成では、Nginx側でヘッダーを付与できます。プリフライトの`OPTIONS`を204で早期に返すのが定石です。[nginx公式のngx\_http\_headers\_moduleドキュメント](https://nginx.org/en/docs/http/ngx%5Fhttp%5Fheaders%5Fmodule.html)にあるとおり、`add_header`は既定で2xx・3xxの一部にしか適用されず、`always`を付けて初めてすべてのステータスに付与されます。

```
location /api/ {
    if ($request_method = OPTIONS) {
        add_header Access-Control-Allow-Origin  "https://app.example.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
        add_header Access-Control-Max-Age       600 always;
        add_header Content-Length 0;
        return 204;
    }

    add_header Access-Control-Allow-Origin "https://app.example.com" always;
    add_header Vary Origin always;

    proxy_pass http://api_upstream;
}
```

ポイントは、通常レスポンスへの`add_header`に`always`を付ける点です。これを省くと、4xxや5xxのエラーレスポンスにCORSヘッダーが付かず、エラー時だけブラウザがCORS違反として二重に分かりにくいエラーを出します。オリジンを複数許可したい場合は、`map`ディレクティブで許可リストを持ち、一致したときだけ変数を返す方法が確実です。`Vary: Origin`を添えるのは、CDNや中間キャッシュがオリジンごとの応答を混同しないようにするためで、前段にキャッシュを挟む構成では省略できません。Nginxの役割そのものを整理したい場合は[リバースプロキシの仕組みと導入判断](https://www.issoh.co.jp/tech/details/13554/)もあわせて確認してください。

### Apacheのmod\_headersでCORSヘッダーを設定する記述例

Apacheでは`mod_headers`を有効にしたうえで、`.htaccess`または仮想ホスト設定にヘッダーを記述します。[Apache HTTP Server 2.4のmod\_headersドキュメント](https://httpd.apache.org/docs/2.4/mod/mod%5Fheaders.html)では、エラー応答にもヘッダーを付けるための修飾子として`always`が定義されており、Nginxの`always`と同じ役割を持ちます。

```
<IfModule mod_headers.c>
    Header always set Access-Control-Allow-Origin  "https://app.example.com"
    Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
    Header always set Access-Control-Allow-Headers "Authorization, Content-Type"
    Header always set Access-Control-Max-Age       "600"
    Header always append Vary Origin
</IfModule>

RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
```

末尾の`RewriteRule`がプリフライトを204で返すための記述です。これが無いとOPTIONSがアプリケーション側へ流れ、405で弾かれる場合があります。単一オリジン固定なら上記で足りますが、動的に許可オリジンを判定したいならアプリケーション層に寄せたほうが管理しやすくなります。

### ExpressとSpring Bootでのアプリケーション層のCORS設定

Node.jsのExpressでは[expressjs/corsミドルウェア](https://github.com/expressjs/cors)が広く使われます。npmレジストリ上の最新は2.8系（2026年9月時点で2.8.6）で、`origin`に関数を渡すと許可リストとの照合を自前で書けます。

```
const express = require("express");
const cors = require("cors");
const app = express();

const allowList = [
  "https://app.example.com",
  "http://localhost:3000",
];

app.use(cors({
  origin: (origin, callback) => {
    // サーバー間呼び出しは Origin が無いので通す
    if (!origin || allowList.includes(origin)) {
      return callback(null, true);
    }
    return callback(new Error("Origin not allowed: " + origin));
  },
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Authorization", "Content-Type"],
  credentials: true,
  maxAge: 600,
}));

app.listen(8000);
```

Spring Bootでは`@CrossOrigin`アノテーションをコントローラーに付ける方法と、`WebMvcConfigurer`で全体設定する方法があります。全体設定なら`addCorsMappings`でエンドポイントごとの付け忘れを防げます。

```
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("https://app.example.com")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("Authorization", "Content-Type")
                .allowCredentials(true)
                .maxAge(600);
    }
}
```

[Spring Framework公式のCORSリファレンス](https://docs.spring.io/spring-framework/reference/web/webmvc-cors.html)は、ワイルドカードと`allowCredentials`の併用について明示的に注意を促しており、可能なかぎり有限の値集合を設定するよう推奨しています。SpringとReactを組み合わせた具体的な配線は[Spring BootとReactを連携する方法（REST API・CORS・JWT認証）](https://www.issoh.co.jp/tech/details/1542/)で手順として追えます。どのフレームワークでも、設定値の実体は同じHTTPレスポンスヘッダーである点は変わりません。フレームワークが違っても、ブラウザに届くのは`Access-Control-Allow-Origin`などのヘッダーです。

## curlでプリフライトを送りCORSヘッダーの返り値を確かめる手順

設定を書いたら、ブラウザで試す前にcurlで確かめます。ブラウザ経由だとキャッシュ・拡張機能・サービスワーカーが挟まり、サーバーが本当に何を返しているのかが見えません。curlならプリフライトを手で再現でき、返却ヘッダーをそのまま読めます。

### curlのverboseでOPTIONSの応答ヘッダーを目視で確認する

プリフライトはブラウザが自動生成しますが、中身はただのOPTIONSリクエストです。`Origin`・`Access-Control-Request-Method`・`Access-Control-Request-Headers`の3つを手で付ければ同じものを送れます。

```
curl -i -X OPTIONS "https://api.example.com/api/users" \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: PUT" \
  -H "Access-Control-Request-Headers: authorization,content-type"
```

返ってくるべき応答は次の形です。ステータスが204または200で、許可メソッドと許可ヘッダーに要求したものが含まれていれば、プリフライトは通ります。

```
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 600
Vary: Origin
```

ここで`Access-Control-Allow-Origin`が出てこないなら、サーバー設定が効いていないか、OPTIONSが別の経路（認証ミドルウェアや404ハンドラ）に吸われています。404や405が返るならルーティングの問題で、CORSの設定値を直しても解決しません。切り分けの順番として、まずこのcurlでサーバー単体の応答を確定させ、それからブラウザに戻ります。

### Access-Control-Allow-Originの一致判定を末尾まで検証する

許可オリジンの文字列は完全一致で判定されます。末尾のスラッシュ、`http`と`https`の違い、`www`の有無、明示したポート番号のいずれかが食い違えば不一致です。`https://app.example.com/`のように末尾へスラッシュを付けた設定は、ブラウザが送る`Origin: https://app.example.com`と一致しません。curlの`-H "Origin: ..."`に本番と同じ文字列を入れて叩き、返却された値と1文字ずつ突き合わせるのが確実な確認方法です。動的に許可リストを照合する実装なら、許可外のオリジンを渡したときにヘッダーが返らないことも同時に確かめます。許可していないオリジンにまでヘッダーが返るなら、その設定は許可リストとして機能していません。

### fetchの実コードでプリフライトの発生条件を切り替えて試す

プリフライトが飛ぶ条件は、フロント側の書き方で切り替えられます。次の2つを並べて開発者ツールのネットワークタブを見ると、OPTIONSが出る場合と出ない場合の差がそのまま観察できます。

```
// (1) プリフライトが発生しない書き方（シンプルリクエスト）
await fetch("https://api.example.com/api/ping", {
  method: "POST",
  headers: { "Content-Type": "text/plain" },
  body: "ping",
});

// (2) プリフライトが発生する書き方（JSON＋認証ヘッダー＋Cookie）
await fetch("https://api.example.com/api/users", {
  method: "PUT",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + token,
  },
  credentials: "include",
  body: JSON.stringify({ name: "taro" }),
});
```

(1)ではOPTIONSが出ず、(2)では同じURLに対してOPTIONSが1本先行します。`Access-Control-Max-Age`を設定していれば、2回目以降の(2)ではOPTIONSが消える仕組みです。消えない場合はヘッダーが返っていないか、ブラウザ側の上限で丸められています。この観察でプリフライトの有無を自分の手で再現できるようになると、エラーの切り分けが推測ではなくなります。

## ブラウザのエラーメッセージから不足ヘッダーを逆算する切り分け手順

「Blocked by CORS policy」という一文だけでは原因は分かりません。ブラウザの開発者ツールに出るエラー文の後半に、不足しているものが必ず書かれています。文言ごとに対処を対応させます。

### ブラウザの開発者ツールに出るエラーメッセージ別の原因と対処法

| エラー文（抜粋）                  | 原因         | 対処               |
| ------------------------- | ---------- | ---------------- |
| No Allow-Origin header    | 許可ヘッダー未返却  | サーバーでヘッダー設定      |
| origin does not match     | 許可オリジン不一致  | 末尾やhttpsの綴りを確認   |
| Method PUT is not allowed | 許可メソッド不足   | Allow-Methodsに追加 |
| header is not allowed     | 許可ヘッダー不足   | Allow-Headersに追加 |
| credentials mode error    | 認証付きで\*を使用 | \*を具体オリジンに変更     |
| redirect is not allowed   | プリフライトが転送  | OPTIONSの転送を止める   |

切り分けの順序は、まずネットワークタブでOPTIONSリクエストの有無とそのステータスを見ることです。OPTIONSが失敗していればプリフライト設定の問題、OPTIONSは通るのに本リクエストで落ちるなら許可メソッドやヘッダーの不足、と切り分けられます。OPTIONS自体が一覧に現れないなら、そもそもシンプルリクエストとして扱われているので、原因はプリフライトではなく通常応答側のヘッダーです。

### ワイルドカードとCredentialsを併用できない仕様上の制約

認証を伴うAPIで最も踏みやすい落とし穴がこれです。`Access-Control-Allow-Origin: *`と、Cookieやトークンを送る`credentials: include`は同時に成立しません。仕様上、Credentialsを許可する場合はオリジンを`*`ではなく具体的な値で返し、`Access-Control-Allow-Credentials: true`を添える必要があります。`Access-Control-Allow-Headers`と`Access-Control-Allow-Methods`のワイルドカードも、Credentials付きのリクエストでは文字どおりのアスタリスクとして解釈され、許可としては働きません。複数オリジンを許可したいときは、リクエストの`Origin`ヘッダーを見て許可リストに含まれるならそのオリジンをそのまま返す、という動的処理をサーバー側で実装します。ここは静的な`*`設定では解決できません。

### 開発中のプロキシによるCORS回避を本番へ持ち込まない実装判断

ここは立場を明確にします。ローカル開発では、開発サーバーのプロキシ機能（Viteの`server.proxy`など）でフロントとAPIを同一オリジンに見せ、CORSを回避する手法が使えます。これは開発の手数を減らす有効な手段です。ただし本番でこの回避に頼るのは避けるべきです。プロキシ回避はCORSを設定不要にしているのではなく、同一オリジンに見せて発生させないだけで、本番のドメインが別オリジンなら結局サーバー設定が要ります。開発中だけ通って本番で失敗する典型パターンがこれです。ブラウザ拡張でCORSを無効化する確認も、自分の環境でしか通らず、動作確認の証拠になりません。前節のcurlによる検証を通していれば、この取り違えは起きません。

## CORSヘッダーを置く層と許可範囲を決める本番構成の判断基準

設定方法が分かった後に残るのは「どこに置くか」「どこまで許すか」です。ここを決めずに各層へ書き足すと、同じヘッダーが2回返るなどの事故が起きます。判断の軸を3つに分けて言い切ります。

### CORSヘッダーをプロキシ層とアプリ層のどちらに集約するか決める

許可オリジンが固定で、APIが単一のサービスなら、リバースプロキシ層に寄せます。設定が1ファイルに収まり、アプリのデプロイと切り離して変更できるからです。一方、テナントごとに許可オリジンが変わる、あるいはエンドポイント単位で許可範囲を分けたい場合は、アプリ層に寄せます。許可判定にデータベースや設定サービスの参照が必要なら、プロキシ層では表現しきれません。どちらに寄せる場合も、もう一方の層では明示的に「CORSヘッダーを付けない」と決め、設定ファイルにその旨をコメントとして残します。この宣言が無いと、後任が片側だけを見て追記し、二重付与を招きます。

### 本番と検証で許可オリジンを切り替える設定値の持たせ方を決める

許可オリジンは環境ごとに違うため、設定ファイルへ直書きせず環境変数や設定ストアから注入します。ここで避けたいのは、検証環境の許可リストに`localhost`を残したまま本番へ配ることです。本番の許可リストに開発用オリジンが混ざると、攻撃者が同じホスト名をローカルに立てた場合に許可が通ります。環境変数名を共通化し、値だけを環境で差し替える構成にしたうえで、本番の値は配信前のチェックリストで目視確認する運用が現実的です。デプロイ後は前述のcurlを本番URLへ1回叩き、返るオリジンが想定どおりかを確かめます。

### APIゲートウェイやCDNを挟む構成でヘッダーが二重になる問題

前段にCDNやAPIゲートウェイを置く構成では、ゲートウェイ側にもCORS機能が用意されていることが多く、オリジン側と両方で有効にすると`Access-Control-Allow-Origin`が2つ返ります。ブラウザはこれを不正な応答として扱うため、許可されているはずのオリジンでもエラーになる状態です。[APIゲートウェイの役割と機能](https://www.issoh.co.jp/tech/details/13210/)を踏まえると、ゲートウェイでOPTIONSを終端する設計とオリジン側で処理する設計は排他で、どちらかに決める必要があります。あわせて、キャッシュ層を挟むなら`Vary: Origin`を必ず返し、あるオリジン向けの応答が別オリジンへ配られないようにします。

CORSヘッダーはWebサーバーやリバースプロキシ、APIゲートウェイで設定する領域です。フロントとAPIを別ドメインで運用する構成なら、設計段階からサーバー側の許可設計を織り込むと後戻りを防げます。こうしたサーバー構成やクラウド基盤の設計を外部に相談したい場合は、[AWS・Google Cloud・Azureのインフラ構築](https://www.issoh.co.jp/service/system/aws/)で、CORS設定を含むAPIサーバーの設計から対応できます。

## よくある質問

CORSの実装でつまずきやすい点を、検索されやすい疑問の形でまとめます。

### CORSエラーはフロントエンドのコードで直せますか？

直せません。CORSの許可を出すのはAPIサーバー側です。フロントで`fetch`のオプションを変えても、サーバーが`Access-Control-Allow-Origin`を返さなければエラーは解消しません。例外は、フロントとAPIを同一オリジンにまとめる構成変更ですが、これはコード修正ではなく配置の変更です。

### CORSの読み方は何ですか？

「コルス」と読まれることが一般的です。Cross-Origin Resource Sharingの略で、日本語ではオリジン間リソース共有と訳されます。仕様の正式な定義は[WHATWGのFetch標準](https://fetch.spec.whatwg.org/)にまとめられており、かつて存在したW3CのCORS勧告はFetch標準へ統合されました。

### Access-Control-Allow-Originに複数のオリジンを指定できますか？

ヘッダーに複数オリジンをカンマ区切りで並べる書き方は仕様上サポートされていません。複数を許可したい場合は、サーバーでリクエストの`Origin`ヘッダーを読み、許可リストに含まれていればそのオリジンだけを動的に返す実装にします。この実装では`Vary: Origin`も併せて返し、キャッシュが別オリジン向けの応答を再利用しないようにします。ワイルドカード`*`は全許可になり、認証付きリクエストでは使えない制約もあるため、許可範囲は明示的に絞るのが安全です。

### プリフライトリクエストを無くすことはできますか？

リクエストをシンプルリクエストの条件内に収めれば発生しません。ただしJSON APIで`Content-Type: application/json`や認証ヘッダーを使うと条件を外れ、プリフライトが飛びます。現実的には無くすより、`Access-Control-Max-Age`でプリフライト結果をキャッシュして回数を減らす方向が実装しやすい対処です。Chromium系では上限が7200秒なので、それ以上を指定しても効果は伸びません。

### CORSはセキュリティ対策として十分ですか？

CORSは同一オリジンポリシーの緩和ルールであって、それ単体でAPIを守る認証・認可の仕組みではありません。CORSで許可したオリジンからのアクセスでも、サーバー側の認証・入力検証は別途必要です。ブラウザを経由しないcurlやサーバー間通信にはCORSがまったく効かない点も押さえてください。CORS設定を緩くすればセキュリティが下がりますが、厳しくすれば安全になるわけでもなく、あくまで「どのオリジンにレスポンスを読ませるか」を制御する層だと位置づけるのが正確です。

## 関連記事

- [Same-Origin Policy（同一オリジンポリシー）の基本的な仕組みと役割](https://www.issoh.co.jp/tech/details/7544/)：CORSが緩和している制約そのものの解説です。
- [リバースプロキシとは？仕組み・フォワードプロキシとの違いと導入判断](https://www.issoh.co.jp/tech/details/13554/)：CORSヘッダーを付与する前段レイヤーの選び方です。
- [APIゲートウェイとは？役割・機能とリバースプロキシとの違い](https://www.issoh.co.jp/tech/details/13210/)：ヘッダー二重付与を避ける層の切り分けに使えます。
- [Spring BootとReactを連携する方法｜REST API・CORS・JWT認証まで実装解説](https://www.issoh.co.jp/tech/details/1542/)：本記事の設定をSPA構成へ落とし込む手順です。
- [コンテンツセキュリティポリシー（CSP）とは？nonce方式の設定とXSS防御](https://www.issoh.co.jp/tech/details/16358/)：CORSと併用するブラウザ側の別レイヤーです。
- [インフラ構築（AWS・Google Cloud・Azure）](https://www.issoh.co.jp/service/system/aws/)：CORSヘッダーを設定するWebサーバー・リバースプロキシ・APIゲートウェイの構築を含む、クラウド基盤の設計・実装サービスです。

---

出典: [CORSとは？仕組み・プリフライト・サーバー設定例・エラー解決を実装目線で解説](<https://www.issoh.co.jp/tech/details/13763/>)（株式会社一創）
