ReactでCORSエラーが出る|APIアクセスの基本知識

ReactでCORSエラーが発生する原因と解決方法を解説。Access to fetch blocked by CORS policyの対処法から、プロキシ設定、サーバーサイドでの対応まで初心者向けに詳しく説明します。

Learning Next 運営
27 分で読めます

「ReactでAPIを呼び出したら、急にエラーが出た!」 「CORSって何?どうやって解決するの?」

そんな経験はありませんか?

ReactでAPIを使った開発をしていると、CORSエラーに遭遇することがよくあります。 このエラーは初心者にとって特に分かりにくく、解決方法も複雑に感じがちです。

でも大丈夫です! この記事を読めば、CORSエラーの原因から解決方法まで、すっきりと理解できるようになります。

開発環境での対処法から本番環境での設定まで、実践的な解決方法を詳しく解説します。 一緒に、CORSエラーを恐れずに済むようになりましょう。

CORSエラーって何?基本から理解しよう

CORSエラーは、ブラウザが安全のために起こすエラーです。 まずは基本的な概念から理解していきましょう。

CORSの正体を知ろう

CORSは「Cross-Origin Resource Sharing」の略です。 日本語では「異なるオリジン間でのリソース共有」という意味になります。

オリジンとは、以下の3つの要素で構成されます。

// オリジンの構成要素
プロトコル://ドメイン:ポート

// 例
http://localhost:3000      // React開発サーバー
https://api.example.com    // API サーバー

この2つは異なるオリジンのため、CORSの制約を受けます。 なぜなら、プロトコルとドメインが違うからです。

なぜCORSエラーが発生するの?

ブラウザには同一オリジンポリシーという安全機能があります。 これが、CORSエラーを引き起こす原因です。

// 同一オリジンの例
http://localhost:3000/page1
http://localhost:3000/page2  // ✅ 同じオリジン

// 異なるオリジンの例
http://localhost:3000        // React アプリ
https://api.example.com      // ❌ 違うオリジン
http://localhost:8080        // ❌ ポートが違う

この制限により、悪意のあるサイトから不正なアクセスを防いでいます。 つまり、CORSエラーはセキュリティ機能なんです。

実際のエラーを見てみよう

ReactでAPIを呼び出すと、こんなエラーが発生します。

// ❌ CORSエラーが発生するコード
function UserList() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    fetch('https://api.example.com/users')
      .then(response => response.json())
      .then(data => setUsers(data))
      .catch(error => {
        console.error('CORS Error:', error);
        // エラーメッセージ:
        // Access to fetch at 'https://api.example.com/users' 
        // from origin 'http://localhost:3000' has been blocked by CORS policy
      });
  }, []);
  
  return (
    <div>
      {users.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}

このコードを実行すると、ブラウザがCORSエラーを発生させます。

エラーメッセージを見ると、http://localhost:3000からhttps://api.example.comへのアクセスが「blocked by CORS policy」と表示されています。 これが、異なるオリジン間でのアクセスが制限されている証拠です。

CORSが必要な理由

なぜこんな制限があるのでしょうか? 実は、セキュリティと利便性のバランスを取るためです。

CORSの仕組みにより、以下のことが実現できます。

  • セキュリティの確保:不正なサイトからのアクセスを防ぐ
  • 正当なアクセスの許可:必要な場合のみクロスオリジンアクセスを許可
  • APIの適切な制限:サーバー側でアクセス制御を行う

これらのバランスを取るために、CORSの仕組みが存在するんです。

開発環境での解決方法を覚えよう

開発環境では、いくつかの簡単な方法でCORSエラーを解決できます。 それぞれの方法を詳しく見ていきましょう。

方法1: package.jsonでのプロキシ設定

Create React Appを使っている場合は、package.jsonで簡単に設定できます。

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "proxy": "https://api.example.com",
  "dependencies": {
    // 他の依存関係...
  }
}

この設定を追加するだけで、プロキシが有効になります。

設定後は、コードを以下のように変更します。

// ✅ プロキシ設定後のコード
function UserList() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    // プロキシ経由でアクセス
    fetch('/api/users')  // https://api.example.com/api/users にプロキシされる
      .then(response => response.json())
      .then(data => setUsers(data))
      .catch(error => {
        console.error('API Error:', error);
      });
  }, []);
  
  return (
    <div>
      {users.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}

プロキシ経由でアクセスすることで、CORSエラーが解決されます。 React開発サーバーが代わりにAPIにアクセスしてくれるためです。

方法2: setupProxy.jsでの詳細設定

より詳細な設定が必要な場合は、src/setupProxy.jsを作成します。

// src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'https://api.example.com',
      changeOrigin: true,
      pathRewrite: {
        '^/api': '', // /api を削除
      },
    })
  );
};

この設定により、より柔軟なプロキシ設定が可能になります。

例えば、以下のような設定もできます。

// 複数のAPIエンドポイントを設定
module.exports = function(app) {
  // ユーザーAPI
  app.use(
    '/api/users',
    createProxyMiddleware({
      target: 'https://user-api.example.com',
      changeOrigin: true,
      pathRewrite: {
        '^/api/users': '/users',
      },
    })
  );
  
  // 商品API
  app.use(
    '/api/products',
    createProxyMiddleware({
      target: 'https://product-api.example.com',
      changeOrigin: true,
      pathRewrite: {
        '^/api/products': '/products',
      },
    })
  );
};

この方法なら、異なるAPIサーバーにもまとめてアクセスできます。

方法3: 環境変数を使った設定

環境に応じてAPIエンドポイントを変更したい場合は、環境変数を使います。

// .env.development
REACT_APP_API_URL=http://localhost:8080

// .env.production
REACT_APP_API_URL=https://api.example.com

コード側では、環境変数を使ってAPIを呼び出します。

// ✅ 環境変数を使用したAPI呼び出し
function UserList() {
  const [users, setUsers] = useState([]);
  const apiUrl = process.env.REACT_APP_API_URL;
  
  useEffect(() => {
    fetch(`${apiUrl}/users`)
      .then(response => response.json())
      .then(data => setUsers(data))
      .catch(error => {
        console.error('API Error:', error);
      });
  }, [apiUrl]);
  
  return (
    <div>
      {users.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}

この方法により、開発環境と本番環境で異なるAPIを使い分けられます。

開発環境ではlocalhost:8080、本番環境ではhttps://api.example.comに自動的に切り替わります。

どの方法を選ぶべき?

それぞれの方法には、以下のような特徴があります。

package.jsonのproxy設定

  • 簡単で設定が少ない
  • 単一のAPIサーバーに適している
  • 初心者におすすめ

setupProxy.js

  • 複数のAPIサーバーに対応
  • 詳細な設定が可能
  • 複雑なプロジェクトに適している

環境変数

  • 環境ごとの設定が可能
  • デプロイ時の柔軟性が高い
  • 本番環境も考慮したい場合におすすめ

プロジェクトの規模や要件に応じて、適切な方法を選びましょう。

サーバーサイドでの対応を学ぼう

本番環境では、サーバーサイドでCORSヘッダーを設定する必要があります。 適切な設定方法を覚えましょう。

Express.jsでの基本設定

Node.jsとExpress.jsを使った設定例を見てみましょう。

// Node.js + Express.js の例
const express = require('express');
const cors = require('cors');
const app = express();

// CORS設定
app.use(cors({
  origin: ['http://localhost:3000', 'https://myapp.com'],
  credentials: true,
  optionsSuccessStatus: 200
}));

// API エンドポイント
app.get('/api/users', (req, res) => {
  res.json([
    { id: 1, name: '田中太郎' },
    { id: 2, name: '山田花子' }
  ]);
});

app.listen(8080, () => {
  console.log('サーバーが起動しました');
});

この設定により、指定したオリジンからのアクセスが許可されます。

設定の詳細を説明します。

  • origin: アクセスを許可するオリジンを配列で指定
  • credentials: クッキーなどの認証情報を含むリクエストを許可
  • optionsSuccessStatus: プリフライトリクエストのステータスコード

手動でのCORSヘッダー設定

corsライブラリを使わずに、手動でヘッダーを設定することもできます。

// 手動でのCORSヘッダー設定
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  
  if (req.method === 'OPTIONS') {
    res.sendStatus(200);
  } else {
    next();
  }
});

この設定により、指定したオリジンからのアクセスが許可されます。

各ヘッダーの意味を説明します。

  • Access-Control-Allow-Origin: アクセスを許可するオリジン
  • Access-Control-Allow-Methods: 許可するHTTPメソッド
  • Access-Control-Allow-Headers: 許可するリクエストヘッダー

プリフライトリクエストとは?

複雑なリクエストの場合、ブラウザは事前にプリフライトリクエストを送信します。

// プリフライトリクエストが発生するケース
fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  },
  body: JSON.stringify({ name: '新しいユーザー' })
});

このようなリクエストでは、まずOPTIONSリクエストが送信されます。 そのため、サーバー側でOPTIONSメソッドに対応する必要があります。

プリフライトリクエストの対応例を見てみましょう。

// プリフライトリクエストの対応
app.options('*', cors({
  origin: ['http://localhost:3000', 'https://myapp.com'],
  credentials: true,
  allowedHeaders: ['Content-Type', 'Authorization'],
  methods: ['GET', 'POST', 'PUT', 'DELETE']
}));

この設定により、プリフライトリクエストが正しく処理されます。

本番環境での安全な設定を身につけよう

本番環境では、より注意深い設定が必要です。 セキュリティを保ちながら機能を実現する方法を学びましょう。

適切なオリジンの設定

本番環境では、必要最小限のオリジンのみを許可します。

// ✅ 本番環境での安全な設定
const allowedOrigins = [
  'https://myapp.com',
  'https://www.myapp.com',
  'https://staging.myapp.com'
];

app.use(cors({
  origin: function (origin, callback) {
    // APIクライアント(Postmanなど)からのアクセスを許可
    if (!origin) return callback(null, true);
    
    if (allowedOrigins.indexOf(origin) !== -1) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true
}));

この設定により、許可されたオリジンからのみアクセスが可能になります。

関数での判定により、動的にオリジンをチェックできます。 不正なオリジンからのアクセスは、エラーで拒否されます。

環境変数を使った柔軟な設定

環境変数を使うことで、環境ごとに設定を変更できます。

// 環境変数を使用した柔軟な設定
const allowedOrigins = process.env.ALLOWED_ORIGINS 
  ? process.env.ALLOWED_ORIGINS.split(',')
  : ['http://localhost:3000'];

app.use(cors({
  origin: allowedOrigins,
  credentials: true
}));

環境変数の設定例を見てみましょう。

# 開発環境
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001

# 本番環境
ALLOWED_ORIGINS=https://myapp.com,https://www.myapp.com

この方法により、環境に応じて適切なオリジンを設定できます。

よくある間違いと対処法

CORSエラーを解決する際の、よくある間違いを確認しましょう。

間違い1: クライアントサイドでの解決を試みる

// ❌ 間違ったアプローチ
fetch('https://api.example.com/users', {
  mode: 'no-cors'  // レスポンスが読み取れなくなる
});

no-corsモードを使うと、レスポンスが読み取れなくなります。 CORSエラーは、サーバーサイドで適切に設定することが重要です。

間違い2: 本番環境でのワイルドカード使用

// ❌ セキュリティリスクのある設定
app.use(cors({
  origin: '*',  // すべてのオリジンを許可(危険)
  credentials: true
}));

// ✅ 適切な設定
app.use(cors({
  origin: ['https://myapp.com'],  // 特定のオリジンのみ許可
  credentials: true
}));

本番環境では、必要最小限のオリジンのみを許可しましょう。 ワイルドカード(*)は、セキュリティリスクを高めます。

間違い3: credentialsとワイルドカードの組み合わせ

// ❌ 無効な設定
app.use(cors({
  origin: '*',
  credentials: true  // この組み合わせは動作しない
}));

// ✅ 正しい設定
app.use(cors({
  origin: ['https://myapp.com'],
  credentials: true
}));

credentials: trueを使う場合、originにワイルドカードは使えません。 必ず具体的なオリジンを指定しましょう。

開発ツールでのデバッグ方法を覚えよう

CORSエラーを効率的にデバッグする方法を身につけましょう。

ブラウザの開発者ツールでの確認

開発者ツールを使って、CORSエラーの詳細を確認できます。

// デバッグ用のコード
async function fetchWithErrorHandling() {
  try {
    const response = await fetch('https://api.example.com/users');
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const data = await response.json();
    console.log('Success:', data);
  } catch (error) {
    console.error('Error details:', error);
    
    // CORSエラーかどうかを確認
    if (error.name === 'TypeError' && error.message.includes('fetch')) {
      console.error('This might be a CORS error');
    }
  }
}

エラーメッセージを詳しく確認することで、問題の原因を特定できます。

ネットワークタブでのリクエスト確認

ブラウザのネットワークタブを使って、以下を確認しましょう。

  1. リクエストが送信されているか
  2. プリフライトリクエストが発生しているか
  3. レスポンスヘッダーにCORS設定があるか

特に、レスポンスヘッダーの以下の項目を確認します。

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

これらのヘッダーが正しく設定されているかチェックしましょう。

コンソールでのエラー確認

コンソールに表示されるエラーメッセージから、問題を特定できます。

// 典型的なCORSエラーメッセージ
Access to fetch at 'https://api.example.com/users' 
from origin 'http://localhost:3000' 
has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

このメッセージから、Access-Control-Allow-Originヘッダーが不足していることが分かります。

代替手段とベストプラクティス

CORSエラーを根本的に回避する方法も学びましょう。

サーバーサイドレンダリング(SSR)の活用

Next.jsなどのSSRフレームワークを使うと、CORSエラーを回避できます。

// Next.js でのAPI Routes使用例
// pages/api/users.js
export default async function handler(req, res) {
  const response = await fetch('https://api.example.com/users');
  const data = await response.json();
  res.status(200).json(data);
}

// pages/users.js
export async function getServerSideProps() {
  const response = await fetch('http://localhost:3000/api/users');
  const users = await response.json();
  
  return {
    props: { users }
  };
}

SSRを使用することで、サーバーサイドでAPI呼び出しを行い、CORSエラーを回避できます。

BFF(Backend for Frontend)パターン

BFFパターンを使って、外部APIへのアクセスを中継する方法もあります。

// BFFサーバーでのAPIプロキシ
app.get('/api/users', async (req, res) => {
  try {
    const response = await fetch('https://external-api.example.com/users', {
      headers: {
        'Authorization': `Bearer ${process.env.API_TOKEN}`
      }
    });
    
    const data = await response.json();
    res.json(data);
  } catch (error) {
    res.status(500).json({ error: 'API access failed' });
  }
});

BFFパターンにより、外部APIへのアクセスを中継し、CORSエラーを回避できます。

この方法なら、認証情報も安全に管理できます。 また、複数の外部APIを組み合わせて、クライアントに最適なデータを提供することも可能です。

まとめ:CORSエラーを恐れずに開発しよう

ReactでCORSエラーを解決するための重要なポイントをまとめます。

今回学んだ大切なこと

CORSエラーの基本

  • CORSエラーはブラウザのセキュリティ機能
  • 異なるオリジン間でのアクセスを制限
  • セキュリティと利便性のバランスを取る仕組み

開発環境での対処法

  • package.jsonのproxy設定が最も簡単
  • setupProxy.jsで詳細な設定が可能
  • 環境変数で柔軟な設定を実現

本番環境での設定

  • サーバーサイドでCORSヘッダーを設定
  • 必要最小限のオリジンのみを許可
  • 環境変数を使った安全な設定

実践で使えるアドバイス

開発時のコツ

  • まずはpackage.jsonのproxy設定から試す
  • エラーメッセージを丁寧に読む
  • ネットワークタブでリクエストを確認する

本番環境での注意点

  • ワイルドカードの使用は避ける
  • 環境変数で設定を管理する
  • 定期的に許可オリジンを見直す

チーム開発での工夫

  • CORS設定をドキュメント化する
  • 環境ごとの設定を明確にする
  • デプロイ時のチェックリストに含める

CORSエラーは初めて遭遇すると難しく感じますが、原因を理解すれば適切に対処できます。 セキュリティを意識しながら、必要最小限の設定で解決することが大切です。

この記事で学んだ知識を使って、CORSエラーを恐れずに開発を進めていきましょう。 きっと、より安全で効率的なWebアプリケーションを作れるようになるはずです。

ぜひ実際のプロジェクトで試してみてくださいね!

関連記事