CORSとは?フロントエンド開発で発生するCORSエラーの原因と対応方法を解説

はじめに

ReactやVue.js、React Nativeなどを利用してフロントエンドの開発を行っていると、外部APIからデータを取得する場面があります。

例えば、JavaScriptからfetchやaxiosを利用して外部サービスのREST APIへアクセスした際、次のようなエラーが表示されることがあります。

Access to fetch at 'https://example.com/api'
from origin 'http://localhost:3000'
has been blocked by CORS policy

また、次のようなエラーが表示される場合もあります。

Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present
on the requested resource.

これは**CORS(Cross-Origin Resource Sharing)**に関するエラーです。

今回は、CORSとはどのような仕組みなのか、なぜCORSエラーが発生するのか、そしてフロントエンド開発でどのように対応すればよいのかを解説します。

CORSとは

CORSとは、

Cross-Origin Resource Sharing

の略称で、日本語では「オリジン間リソース共有」と呼ばれます。

簡単に説明すると、

異なるWebサイトやドメイン間でデータをやり取りする際のアクセス制御の仕組み

です。

Webブラウザには、セキュリティ対策として「Same-Origin Policy(同一オリジンポリシー)」という仕組みがあります。

基本的には、JavaScriptから異なるオリジンのサーバーへ自由にアクセスすることはできません。

例えば、次のWebアプリがあるとします。

http://localhost:3000

ここから、次の外部APIへアクセスします。

https://api.example.com/users

この場合、

localhost:3000

と、

api.example.com

は異なるオリジンになります。

そのため、APIサーバー側がアクセスを許可していなければ、ブラウザによって通信が制限されます。

オリジンとは

CORSを理解するうえで重要なのが「オリジン」です。

オリジンは基本的に、次の3つの組み合わせによって決まります。

  • プロトコル
  • ホスト名
  • ポート番号

例えば、

https://example.com:443

であれば、

プロトコル:https
ホスト名:example.com
ポート番号:443

となります。

どれか1つでも異なれば、基本的には異なるオリジンとして扱われます。

例えば、

http://example.com

と、

https://example.com

はHTTPとHTTPSが異なるため別オリジンです。

また、

http://localhost:3000

と、

http://localhost:5173

についても、ポート番号が異なるため別オリジンとして扱われます。

なぜCORSが必要なのか

もしCORSのような制限がなければ、悪意のあるWebサイトから別のWebサービスへ勝手に通信できてしまう可能性があります。

例えば、ユーザーがあるWebサービスへログインした状態で、悪意のあるサイトを開いたとします。

そのサイトのJavaScriptから自由に別サービスへアクセスできてしまうと、ユーザーが意図しない操作やデータ取得が行われる可能性があります。

そのためブラウザでは、異なるオリジン間の通信を制限しています。

CORSは単なるエラーではなく、Webブラウザの重要なセキュリティ機能の一つです。

CORSエラーが発生する例

例えばReactから、次のように外部APIへアクセスしたとします。

fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(data => {
    console.log(data);
  });

APIサーバーがCORSを許可していない場合、ブラウザ側では次のようなエラーが発生することがあります。

Access to fetch at 'https://api.example.com/users'
from origin 'http://localhost:3000'
has been blocked by CORS policy

重要なのは、

fetchの書き方が間違っているとは限らない

という点です。

CORSエラーの場合、原因はAPIサーバー側の設定であることが少なくありません。

Access-Control-Allow-Originとは

CORSでは、APIサーバーがレスポンスヘッダーを利用してアクセス可能なオリジンを指定します。

代表的なものが、

Access-Control-Allow-Origin

です。

例えばAPIサーバーから、

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

というレスポンスヘッダーが返ってきた場合、

https://example.com

からのアクセスを許可する、という意味になります。

すべてのオリジンを許可する場合には、

Access-Control-Allow-Origin: *

と設定することもあります。

ただし、本番環境ではセキュリティ上の理由から、必要なドメインだけを許可するのが一般的です。

Preflight Requestとは

CORSエラーを調査していると、

preflight request

という言葉を見ることがあります。

Preflight Request(プリフライトリクエスト)とは、本来のAPI通信を行う前に、

「この通信を実行してもよいですか?」

とブラウザがAPIサーバーへ確認する通信です。

通常、

OPTIONS

というHTTPメソッドが利用されます。

例えばフロントエンド側で、

fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer TOKEN'
  },
  body: JSON.stringify({
    name: 'Taro'
  })
});

のような通信を行った場合、先にOPTIONSリクエストが送信されることがあります。

イメージとしては、

ブラウザ
   ↓
OPTIONS /users
   ↓
APIサーバー
   ↓
アクセス許可を返す
   ↓
POST /users

という流れです。

APIサーバーがOPTIONSリクエストへ正しく応答しない場合、

Response to preflight request doesn't pass access control check

というエラーが発生する場合があります。

CORSエラーの主な原因

CORSエラーには、いくつか代表的な原因があります。

APIサーバーがCORSを許可していない

最も代表的なケースです。

APIサーバーから、

Access-Control-Allow-Origin

が返されていない場合、ブラウザによってブロックされることがあります。

許可されているOriginが異なる

例えばサーバー側で、

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

だけを許可しているにもかかわらず、

http://localhost:3000

からアクセスするとCORSエラーになります。

開発環境の場合、

http://localhost:3000

http://localhost:5173

なども許可する必要があります。


HTTPメソッドが許可されていない

例えば、

GET

のみ許可されているAPIへ、

POST
PUT
DELETE

などを送信するとCORSエラーになる場合があります。

サーバー側では、

Access-Control-Allow-Methods

によって許可するHTTPメソッドを設定します。

例:

Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS

HTTPヘッダーが許可されていない

APIトークンやAuthorizationヘッダーなど、独自のヘッダーを送信する場合にも注意が必要です。

例えば、

headers: {
  Authorization: 'Bearer TOKEN'
}

を送信する場合、APIサーバー側でそのヘッダーが許可されている必要があります。

サーバー側では、

Access-Control-Allow-Headers

を設定します。

例:

Access-Control-Allow-Headers: Content-Type, Authorization

CORSエラーの対応方法

CORSエラーが発生した場合、基本的にはAPIサーバー側で対応する必要があります。

フロントエンド側のJavaScriptだけでCORS制限を解除することはできません。

ここはCORSを理解するうえで非常に重要なポイントです。


対応方法1:APIサーバー側でCORSを許可する

自分で管理しているAPIの場合、サーバー側のCORS設定を変更します。

例えばレスポンスヘッダーとして、

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

を返します。

開発環境であれば、

Access-Control-Allow-Origin: http://localhost:3000

などを設定します。


LaravelでCORSを設定する場合

LaravelでAPIを開発している場合には、CORS設定を利用できます。

Laravelのバージョンによって設定方法は異なりますが、CORS設定では主に次のような項目を指定します。

'allowed_methods' => ['*'],

'allowed_origins' => [
    'http://localhost:3000',
    'https://example.com',
],

'allowed_headers' => ['*'],

本番環境では、

'allowed_origins' => ['*']

としてすべて許可するよりも、

'allowed_origins' => [
    'https://example.com',
]

のように、アクセスを許可するフロントエンドのドメインを指定した方が安全です。


Node.js / ExpressでCORSを設定する場合

Node.jsとExpressでAPIを作成している場合には、corsパッケージを利用できます。

インストールします。

npm install cors

次にExpressへ設定します。

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

const app = express();

app.use(cors({
  origin: 'http://localhost:3000'
}));

app.get('/api/users', (req, res) => {
  res.json([
    {
      id: 1,
      name: 'Taro'
    }
  ]);
});

app.listen(3001);

複数のオリジンを許可したい場合は、環境に応じた設定を行います。


対応方法2:バックエンドを経由してAPIへアクセスする

外部サービスのAPIを利用している場合、自分でAPI側のCORS設定を変更できないことがあります。

例えば、

React
 ↓
外部API

という構成ではCORSエラーになる場合があります。

この場合には、

React
 ↓
自社バックエンド
 ↓
外部API

という構成に変更します。

例えばLaravelを利用している場合、

React
 ↓
Laravel
 ↓
外部API

とします。

Laravel側から外部APIへアクセスします。

use Illuminate\Support\Facades\Http;

$response = Http::get('https://api.example.com/users');

return response()->json($response->json());

ReactからはLaravelのAPIへアクセスします。

const response = await fetch('/api/users');

const data = await response.json();

console.log(data);

この方法には、

  • CORS問題を回避しやすい
  • APIキーをフロントエンドへ公開しなくてよい
  • APIアクセス処理をバックエンド側へ集約できる

というメリットがあります。

特にAPIトークンやAPIキーを利用するサービスの場合には、バックエンド経由にする方法が適しています。


対応方法3:開発サーバーのProxyを利用する

ローカル開発環境では、ViteなどのProxy機能を利用する方法もあります。

例えばViteであれば、vite.config.jsへ次のように設定できます。

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        secure: true,
      },
    },
  },
});

React側では、

fetch('/api/users');

のようにアクセスします。

開発サーバーが、

/api

へのアクセスを、

https://api.example.com

へ中継します。

ただし、この方法は主に開発環境向けの対応方法です。

本番環境では、WebサーバーやバックエンドAPIなどを含めた構成を検討する必要があります。


mode: ‘no-cors’では基本的に解決しない

CORSエラーについて調べると、

fetch(url, {
  mode: 'no-cors'
});

という方法を見かけることがあります。

しかし、

mode: 'no-cors'

は一般的なREST APIからJSONデータを取得したい場合の解決策にはなりません。

レスポンスがopaqueとなり、JavaScriptからレスポンス本文を通常通り読み取れなくなるためです。

そのため、

mode: 'no-cors'

を追加すればCORSエラーを解決できる、とは考えない方がよいでしょう。


Chromeのセキュリティ設定を無効化する方法はおすすめしない

開発時にブラウザのセキュリティを無効化してCORSを回避する方法もインターネット上では紹介されています。

しかし、これはCORSの根本的な解決にはなりません。

自分のブラウザで動作したとしても、通常の環境でアクセスしたユーザーにはCORSエラーが発生します。

そのため、

APIサーバー側のCORS設定を修正する

または、

バックエンドサーバーを経由する

という方法を基本的な対応方針とすることをおすすめします。


axiosに変更してもCORSエラーは解決しない

例えばfetchでCORSエラーが発生したため、axiosへ変更するケースがあります。

axios.get('https://api.example.com/users');

しかし、

fetch → axios

へ変更しただけではCORSエラーは基本的に解決しません。

CORSはブラウザのセキュリティ機能であり、JavaScriptのHTTP通信ライブラリによって発生している問題ではないためです。

そのため、

fetchでCORSエラー

が発生している場合、

axiosならアクセスできる

とは限りません。


Postmanでは成功するのにブラウザでは失敗する理由

CORSを調査するときによくあるのが、

PostmanではAPIアクセスできる
ブラウザからはCORSエラーになる

というケースです。

これは異常ではありません。

CORSは主にWebブラウザが適用するセキュリティ制限だからです。

そのため、

curl
Postman
PHP
Laravel
Node.js

などからAPIへアクセスすると成功するにもかかわらず、

React
Vue.js
ブラウザ上のJavaScript

からアクセスするとCORSエラーになることがあります。

この場合、

API自体にアクセスできないのではなく、ブラウザからのクロスオリジン通信が許可されていない

可能性があります。


CORSエラーが発生したときの確認ポイント

CORSエラーが発生した場合には、次の項目を確認すると原因を特定しやすくなります。

  1. フロントエンドとAPIのOriginが異なっていないか
  2. APIからAccess-Control-Allow-Originが返されているか
  3. 自分のフロントエンドのOriginが許可されているか
  4. OPTIONSリクエストが成功しているか
  5. HTTPメソッドが許可されているか
  6. AuthorizationなどのHTTPヘッダーが許可されているか
  7. API側のCORS設定を変更できるサービスなのか
  8. バックエンド経由にする必要がないか

Chromeなどのブラウザでは、開発者ツールの「Network」タブから通信内容を確認できます。

特に、

OPTIONS

リクエストのStatus CodeやResponse Headersを確認すると、CORSエラーの原因を特定しやすくなります。

まとめ

CORSは、

異なるオリジン間でWebリソースへアクセスする際のセキュリティ制御

です。

ReactやVue.jsなどのフロントエンドから外部APIを利用していると、

blocked by CORS policy

や、

No 'Access-Control-Allow-Origin' header

などのエラーが発生することがあります。

この場合、単純にfetchやaxiosのコードを変更するのではなく、まずAPIサーバー側のCORS設定を確認することが重要です。

自分で管理しているAPIであれば、

Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers

などを適切に設定します。

一方、外部サービスのAPIなど、自分でCORS設定を変更できない場合には、

フロントエンド
   ↓
自社バックエンド
   ↓
外部API

という構成にして、LaravelやNode.jsなどのバックエンドを経由してアクセスする方法も有効です。

特にAPIキーやAPIトークンを利用するサービスでは、認証情報をフロントエンドへ直接記述しないという意味でも、バックエンド経由でアクセスする設計がおすすめです。

CORSエラーが発生した場合には、エラー文だけを見るのではなく、ブラウザの開発者ツールからOPTIONSリクエストやレスポンスヘッダーを確認し、どの段階で通信が拒否されているのかを確認してみましょう。