ReactでCORSエラーが出る|APIアクセスの基本知識
ReactでCORSエラーが発生する原因と解決方法を解説。Access to fetch blocked by CORS policyの対処法から、プロキシ設定、サーバーサイドでの対応まで初心者向けに詳しく説明します。
「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');
}
}
}
エラーメッセージを詳しく確認することで、問題の原因を特定できます。
ネットワークタブでのリクエスト確認
ブラウザのネットワークタブを使って、以下を確認しましょう。
- リクエストが送信されているか
- プリフライトリクエストが発生しているか
- レスポンスヘッダーに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アプリケーションを作れるようになるはずです。
ぜひ実際のプロジェクトで試してみてくださいね!