MeWrite Docs

Access to fetch has been blocked by CORS policy

ブラウザのCORSポリシーによりリクエストがブロックされた場合のエラー

概要

ブラウザのセキュリティ機能であるCORS(Cross-Origin Resource Sharing)ポリシーにより、異なるオリジン間のリクエストがブロックされた場合に発生するエラーです。

エラーメッセージ

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

Access to XMLHttpRequest at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check.

CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.

原因

  1. サーバー側のCORS設定なし: Access-Control-Allow-Originヘッダーがない
  2. プリフライトリクエスト失敗: OPTIONSリクエストへの応答がない
  3. 許可されていないヘッダー: カスタムヘッダーが許可されていない
  4. 認証情報の問題: credentials設定と許可の不一致
  5. HTTPメソッドの制限: 許可されていないメソッドを使用

解決策

1. Express.jsでCORS設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const express = require('express');
const cors = require('cors');
const app = express();

// すべてのオリジンを許可(開発用)
app.use(cors());

// 特定のオリジンを許可(本番用)
app.use(cors({
  origin: 'https://example.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true
}));

2. 手動でCORSヘッダーを設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Express.js
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://example.com');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.header('Access-Control-Allow-Credentials', 'true');

  // プリフライトリクエストへの応答
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }

  next();
});

3. Nginxでの設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
server {
    location /api/ {
        # CORSヘッダー
        add_header 'Access-Control-Allow-Origin' 'https://example.com' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;

        # プリフライトリクエスト
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Max-Age' 86400;
            add_header 'Content-Length' 0;
            return 204;
        }

        proxy_pass http://backend;
    }
}

4. Laravelでの設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// app/Http/Middleware/Cors.php
namespace App\Http\Middleware;

use Closure;

class Cors
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        $response->headers->set('Access-Control-Allow-Origin', 'https://example.com');
        $response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
        $response->headers->set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
        $response->headers->set('Access-Control-Allow-Credentials', 'true');

        return $response;
    }
}

// または fruitcake/laravel-cors パッケージを使用
// config/cors.php
return [
    'paths' => ['api/*'],
    'allowed_origins' => ['https://example.com'],
    'allowed_methods' => ['*'],
    'allowed_headers' => ['*'],
    'supports_credentials' => true,
];

5. Django (django-cors-headers)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# settings.py
INSTALLED_APPS = [
    'corsheaders',
    # ...
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    # ...
]

CORS_ALLOWED_ORIGINS = [
    'https://example.com',
]

CORS_ALLOW_CREDENTIALS = True

CORS_ALLOW_HEADERS = [
    'content-type',
    'authorization',
]

6. Spring Boot

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("https://example.com")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedHeaders("*")
            .allowCredentials(true);
    }
}

7. クライアント側の設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// credentials を含むリクエスト
fetch('https://api.example.com/data', {
  method: 'GET',
  credentials: 'include',  // Cookie を含める
  headers: {
    'Content-Type': 'application/json'
  }
});

// 注意: credentials: 'include' の場合
// サーバー側で Access-Control-Allow-Origin: * は使用できない

8. 開発時のプロキシ設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Vite (vite.config.js)
export default {
  server: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
};
1
2
3
4
// Create React App (package.json)
{
  "proxy": "https://api.example.com"
}

9. AWS API Gateway

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# SAM template
Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      Cors:
        AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
        AllowHeaders: "'Content-Type,Authorization'"
        AllowOrigin: "'https://example.com'"
        AllowCredentials: true

10. CloudFront + Lambda@Edge

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Lambda@Edge (Origin Response)
exports.handler = async (event) => {
  const response = event.Records[0].cf.response;
  const headers = response.headers;

  headers['access-control-allow-origin'] = [{ value: 'https://example.com' }];
  headers['access-control-allow-methods'] = [{ value: 'GET, POST, OPTIONS' }];
  headers['access-control-allow-headers'] = [{ value: 'Content-Type' }];

  return response;
};

CORSの仕組み

リクエスト種別プリフライト条件
Simple Request不要GET/HEAD/POST、特定ヘッダーのみ
Preflight Request必要PUT/DELETE、カスタムヘッダー

よくある間違い

  • Access-Control-Allow-Origin: *credentials: include を併用
  • OPTIONSリクエストを処理し忘れる
  • ヘッダー名の大文字小文字を間違える
  • サーバー側のエラーをCORSエラーと勘違いする

関連エラー

参考リンク

HTTP の他のエラー

最終更新: 2025-12-13