フロントエンド開発を進める中で、ブラウザのコンソールに「Access to fetch … has been blocked by CORS policy」というエラーが表示されて困った経験はないでしょうか?
この記事では、オリジンとは何か・CORSがなぜ必要なのか・プリフライトの仕組みを順序立てて解説し、Vue+Spring Bootを使った具体的な対処法まで紹介します。初学者〜中級者のWeb開発者を対象としています。
この記事で学べること
- オリジン(Origin)の定義と同一オリジン・別オリジンの判定方法
- 同一オリジンポリシー(SOP)とCORSの関係
- プリフライトリクエストが発生する条件と仕組み
- Vue(Viteプロキシ)+ Spring Boot でのCORS対処法
- 主要なCORS関連HTTPヘッダーの意味と注意点
オリジンとは何か
オリジン(Origin)とは、プロトコル・ホスト・ポート番号の3要素の組み合わせのことです。この3つがすべて一致するとき「同一オリジン」、一つでも異なると「別オリジン(クロスオリジン)」と呼びます。

基準オリジン https://example.com:443 を元に、各URLが同一オリジンかどうかを見てみましょう。
| URL | 基準との関係 | 理由 |
|---|---|---|
| https://example.com:443/api/users | ✅ 同一オリジン | パスは判定に関係ない |
| http://example.com:443 | ❌ 別オリジン | プロトコルが異なる |
| https://api.example.com:443 | ❌ 別オリジン | ホストが異なる |
| https://example.com:8080 | ❌ 別オリジン | ポートが異なる |
💡 例え話:マンションのセキュリティ
オリジンを「マンションの住所+部屋番号」に例えると、建物・棟・部屋番号がすべて一致しないと「同じ部屋の住人」とは見なされません。プロトコル・ホスト・ポートのどれか一つでも違えば、ブラウザは「別の場所」と判断します。
CORSの仕組み
ブラウザには同一オリジンポリシー(SOP: Same-Origin Policy)というセキュリティ制約があります。これはデフォルトで、別オリジンへのリクエストで返ってきたレスポンスをJavaScriptに渡さないようにする仕組みです。
CORS(Cross-Origin Resource Sharing)はこの制約を安全に緩和するための仕組みです。サーバー側がHTTPレスポンスヘッダーで「このオリジンからのアクセスは許可する」と宣言することで、ブラウザがレスポンスをJavaScriptに渡すことを許可します。
CORSの基本的な流れは以下のとおりです。

- ブラウザがサーバーへHTTPリクエストを送信する
- サーバーがCORSヘッダー付きのレスポンスを返す
- ブラウザがヘッダーを確認し、許可されていればJavaScriptにレスポンスを渡す。許可されていなければブロックする(=CORSエラー)
⚠️ よくある誤解
CORSエラーが発生しても、サーバーへのリクエスト自体は届いています。ブラウザがレスポンスをJavaScriptに渡さないだけです。そのため、副作用を持つAPIへのリクエスト(POSTなど)は特に注意が必要です。
プリフライトリクエスト
「単純リクエスト」以外の場合、ブラウザは本リクエストを送る前にOPTIONSメソッドによる事前確認(プリフライト)を自動で行います。
プリフライトが不要な「単純リクエスト」の主な条件:
- メソッドが GET / POST / HEAD のいずれか
- Content-Type が application/x-www-form-urlencoded・multipart/form-data・text/plain のいずれか
- カスタムリクエストヘッダーを含まない
プリフライトが必要な代表的なケース:
- Content-Type: application/json を使うPOSTリクエスト
- PUT / DELETE / PATCH メソッドを使うリクエスト
- Authorization ヘッダーを含むリクエスト
プリフライトが発生する場合のフローは次のとおりです。

- ブラウザがOPTIONSメソッドでプリフライトリクエストを送信する
- サーバーが200 OKと許可情報(CORSヘッダー)を返す
- ブラウザが許可を確認し、本リクエスト(POST等)を送信する
- サーバーが本リクエストへのレスポンスを返す
💡 Access-Control-Max-Age によるキャッシュ
サーバーが Access-Control-Max-Age: 3600 を返すことで、ブラウザはその秒数の間、プリフライト結果をキャッシュし、毎回のOPTIONSリクエストを省略できます。パフォーマンス改善に有効です。
Vue + Spring Boot での実装例
以下の構成を前提に、フロントエンド・バックエンドそれぞれの設定例を示します。
| 役割 | 技術 | オリジン |
|---|---|---|
| フロントエンド | Vue(Vite) | https://frontend.com |
| バックエンド | Spring Boot | https://api.backend.com |
Vue側:開発環境のViteプロキシ設定
開発環境ではViteのプロキシ機能を使い、同一オリジンとして扱うことでCORSを回避できます。
// vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
}
}
}
})Spring Boot側:グローバルCORS設定(推奨)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://frontend.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}Spring Security と併用する場合
🚨 注意:Spring Securityを導入している場合、WebMvcConfigurer の設定だけでは効かないことがあります。Security側にも必ずCORS設定を追加してください。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(csrf -> csrf.disable());
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://frontend.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}CORSの主要ヘッダー一覧
レスポンスヘッダー(サーバーが返す)
| ヘッダー名 | 説明 |
|---|---|
| Access-Control-Allow-Origin | 許可するオリジン(例: https://frontend.com または *) |
| Access-Control-Allow-Methods | 許可するHTTPメソッド(例: GET, POST, PUT, DELETE) |
| Access-Control-Allow-Headers | 許可するリクエストヘッダー(例: Content-Type, Authorization) |
| Access-Control-Allow-Credentials | CookieなどのCredentialを許可するか(true を指定) |
| Access-Control-Max-Age | プリフライト結果のキャッシュ時間(秒) |
| Access-Control-Expose-Headers | JavaScriptから読めるレスポンスヘッダーを追加指定 |
リクエストヘッダー(ブラウザが自動で付与)
| ヘッダー名 | 説明 |
|---|---|
| Origin | リクエスト元のオリジン |
| Access-Control-Request-Method | プリフライト時に使用予定のメソッドを通知 |
| Access-Control-Request-Headers | プリフライト時に使用予定のカスタムヘッダーを通知 |
⚠️ ワイルドカード * の注意点Access-Control-Allow-Origin: * は Access-Control-Allow-Credentials: true と同時には使えません。CookieやAuthorizationヘッダーなど認証情報を扱う場合は、ワイルドカードではなく特定のオリジンを明示的に指定してください。
CORSエラーとは何か
CORSエラーとは、「ブラウザがサーバーからのレスポンスをJavaScriptに渡さずブロックすること」そのものを指します。JavaScriptの catch 内では詳細なエラーメッセージは確認できず、TypeError: Failed to fetch しか表示されません。
fetch('https://api.backend.com/users')
.catch(err => {
console.error(err)
// → TypeError: Failed to fetch
})💡 CORSエラーの詳細なメッセージは ブラウザの開発者ツール(コンソールタブ) で確認できます。「blocked by CORS policy」という文言とともに、どのヘッダーが不足しているかが表示されるので、まずそちらを確認しましょう。
まとめ
| テーマ | ポイント |
|---|---|
| オリジン | プロトコル・ホスト・ポートの3要素の組み合わせ。1つでも違えば別オリジン |
| CORSの仕組み | サーバーがレスポンスヘッダーで許可オリジンを宣言。ブラウザが確認して許可・拒否を判断 |
| プリフライト | 複雑なリクエストの前にOPTIONSで事前確認。Max-Ageでキャッシュ可能 |
| Vue + Spring Boot | 開発はViteプロキシ、本番はSpring BootのCORS設定。Spring Security使用時は両方に設定が必要 |
| CORSエラー | ブラウザがJSへのレスポンス渡しをブロックすること。詳細は開発者ツールで確認 |

コメント