DevToolBoxKOSTENLOS
Blog

CORS-Fehler beheben: Der komplette Troubleshooting-Guide

12 Min. Lesezeitvon DevToolBox

CORS-Fehler gehören zu den frustrierendsten Problemen in der Webentwicklung. Sie sehen die gefürchtete rote Konsolenmeldung, Ihr API-Aufruf schlägt fehl, und nichts scheint zu funktionieren. Dieser Guide hilft Ihnen, CORS genau zu verstehen, Ihren spezifischen Fehler zu diagnostizieren und mit der richtigen Lösung zu beheben.

Was ist CORS? (30-Sekunden-Erklärung)

Cross-Origin Resource Sharing (CORS) ist eine Browser-Sicherheitsfunktion, die Webseiten daran hindert, Anfragen an eine andere Domain als die sendende zu stellen. Wenn Ihr Frontend unter http://localhost:3000 Daten von http://api.example.com abrufen möchte, prüft der Browser, ob der API-Server diese Cross-Origin-Anfrage über spezielle HTTP-Header explizit erlaubt.

Wie CORS funktioniert: Der Ablauf

┌─────────────────┐         ┌─────────────────────┐
│   Browser        │         │   API Server         │
│   (Frontend)     │         │   (Backend)          │
│                  │         │                      │
│ http://localhost │ ──────> │ https://api.example  │
│     :3000        │  fetch  │     .com             │
│                  │         │                      │
└─────────────────┘         └─────────────────────┘
                                    │
                    ┌───────────────┴───────────────┐
                    │                               │
              CORS Headers                   No CORS Headers
              Present?                       Present?
                    │                               │
                    ▼                               ▼
          ┌─────────────────┐            ┌──────────────────┐
          │  Response sent  │            │  Browser BLOCKS  │
          │  to JavaScript  │            │  the response    │
          └─────────────────┘            └──────────────────┘

Wenn CORS korrekt konfiguriert ist, antwortet der Server mit Headern, die dem Browser sagen: "Ja, ich erlaube Anfragen von diesem Ursprung."

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json

{"data": "success"}

Wenn CORS NICHT konfiguriert ist, blockiert der Browser die Antwort (auch wenn der Server die Anfrage erfolgreich verarbeitet hat).

HTTP/1.1 200 OK
Content-Type: application/json
# No Access-Control-Allow-Origin header!

{"data": "success"}  <-- Browser received this but BLOCKS JavaScript from reading it

Die 5 häufigsten CORS-Fehlermeldungen

Fehler 1: Kein Access-Control-Allow-Origin Header

No 'Access-Control-Allow-Origin' header is present on the requested resource.

Bedeutung: Der Server hat keinen Access-Control-Allow-Origin Header in seiner Antwort. Dies ist der häufigste CORS-Fehler.

Fehler 2: Wildcard mit Anmeldedaten

The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*' when credentials mode is 'include'.

Bedeutung: Sie senden Cookies oder Authorization-Header, aber der Server antwortet mit *. Bei Anmeldedaten muss der Server den genauen Ursprung angeben.

Fehler 3: Preflight-Anfrage fehlgeschlagen

Response to preflight request doesn't pass access control check.

Bedeutung: Der Browser hat eine OPTIONS-Anfrage (Preflight) gesendet und der Server hat diese nicht korrekt behandelt.

Fehler 4: Methode nicht erlaubt

Method PUT is not allowed by Access-Control-Allow-Methods.

Bedeutung: Der Access-Control-Allow-Methods Header des Servers enthält die verwendete HTTP-Methode nicht.

Fehler 5: Header nicht erlaubt

Request header field X-Custom-Header is not allowed by Access-Control-Allow-Headers.

Bedeutung: Der Server hat den benutzerdefinierten Header nicht in Access-Control-Allow-Headers erlaubt.

Diagnose-Flussdiagramm: Wo liegt das Problem?

Verwenden Sie diesen Entscheidungsbaum, um schnell zu identifizieren, ob Ihr CORS-Problem ein Frontend-, Backend- oder Proxy-Problem ist:

Schritt 1: Öffnen Sie DevTools > Network-Tab. Sehen Sie eine OPTIONS-Anfrage vor Ihrer eigentlichen Anfrage?

JA -> Der Browser sendet eine Preflight-Anfrage. Prüfen Sie, ob die OPTIONS-Antwort Status 200 hat und CORS-Header enthält.

NEIN -> Dies ist eine einfache Anfrage. Das Problem sind fehlende CORS-Header in der Serverantwort.

Schritt 2: Erwähnt die Fehlermeldung "preflight"?

JA -> Backend-Problem. Ihr Server muss OPTIONS-Anfragen behandeln.

NEIN -> Prüfen Sie, ob Sie Anmeldedaten verwenden. Wenn ja, können Sie keinen Wildcard (*) verwenden.

Schritt 3: Funktioniert die API bei direktem Aufruf (curl, Postman)?

JA -> Es ist ein CORS-Problem (nur Browser).

NEIN -> Kein CORS-Problem. Die API selbst hat ein Problem.

Zusammenfassung: 90% der CORS-Fehler werden durch korrekte Header-Konfiguration auf dem Backend-Server gelöst.

Server-seitige Fixes nach Framework

Express.js (Node.js)

// Method 1: Using the cors package (recommended)
npm install cors

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

// Allow specific origins
app.use(cors({
  origin: ['http://localhost:3000', 'https://yoursite.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400 // Cache preflight for 24 hours
}));

// Method 2: Manual middleware
app.use((req, res, next) => {
  const allowedOrigins = ['http://localhost:3000', 'https://yoursite.com'];
  const origin = req.headers.origin;
  if (allowedOrigins.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
  }
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,PATCH');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  res.setHeader('Access-Control-Allow-Credentials', 'true');

  // Handle preflight
  if (req.method === 'OPTIONS') {
    res.setHeader('Access-Control-Max-Age', '86400');
    return res.status(204).end();
  }
  next();
});

Django (Python)

# Install django-cors-headers
pip install django-cors-headers

# settings.py
INSTALLED_APPS = [
    ...
    'corsheaders',
    ...
]

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

# Option 1: Allow specific origins
CORS_ALLOWED_ORIGINS = [
    'http://localhost:3000',
    'https://yoursite.com',
]

# Option 2: Allow all origins (dev only!)
# CORS_ALLOW_ALL_ORIGINS = True

# Allow credentials (cookies, auth headers)
CORS_ALLOW_CREDENTIALS = True

# Allowed headers
CORS_ALLOW_HEADERS = [
    'content-type',
    'authorization',
    'x-requested-with',
]

# Allowed methods
CORS_ALLOW_METHODS = [
    'GET',
    'POST',
    'PUT',
    'PATCH',
    'DELETE',
    'OPTIONS',
]

Flask (Python)

# Install flask-cors
pip install flask-cors

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)

# Option 1: Allow specific origins
CORS(app, origins=['http://localhost:3000', 'https://yoursite.com'],
     supports_credentials=True,
     allow_headers=['Content-Type', 'Authorization'],
     methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH'])

# Option 2: Manual approach
@app.after_request
def after_request(response):
    origin = request.headers.get('Origin')
    allowed = ['http://localhost:3000', 'https://yoursite.com']
    if origin in allowed:
        response.headers['Access-Control-Allow-Origin'] = origin
        response.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE'
        response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization'
        response.headers['Access-Control-Allow-Credentials'] = 'true'
    return response

Go (net/http)

package main

import (
    "net/http"
    "strings"
)

func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        allowedOrigins := map[string]bool{
            "http://localhost:3000": true,
            "https://yoursite.com": true,
        }

        origin := r.Header.Get("Origin")
        if allowedOrigins[origin] {
            w.Header().Set("Access-Control-Allow-Origin", origin)
            w.Header().Set("Access-Control-Allow-Methods",
                strings.Join([]string{"GET","POST","PUT","DELETE","PATCH"}, ","))
            w.Header().Set("Access-Control-Allow-Headers",
                "Content-Type, Authorization")
            w.Header().Set("Access-Control-Allow-Credentials", "true")
            w.Header().Set("Access-Control-Max-Age", "86400")
        }

        // Handle preflight
        if r.Method == http.MethodOptions {
            w.WriteHeader(http.StatusNoContent)
            return
        }

        next.ServeHTTP(w, r)
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/api/data", handleData)

    handler := corsMiddleware(mux)
    http.ListenAndServe(":8080", handler)
}

Nginx-Konfiguration

server {
    listen 80;
    server_name api.example.com;

    # CORS headers for all locations
    set $cors_origin "";
    if ($http_origin ~* "^https?://(localhost:3000|yoursite\.com)$") {
        set $cors_origin $http_origin;
    }

    location /api/ {
        # CORS headers
        add_header 'Access-Control-Allow-Origin' $cors_origin always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Max-Age' 86400 always;

        # Handle preflight requests
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' $cors_origin always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;
            add_header 'Access-Control-Allow-Credentials' 'true' always;
            add_header 'Access-Control-Max-Age' 86400;
            add_header 'Content-Length' 0;
            add_header 'Content-Type' 'text/plain';
            return 204;
        }

        proxy_pass http://backend:8080;
    }
}

Reverse Proxy als CORS-Middleware

Wenn Sie die Backend-API nicht ändern können (z.B. Drittanbieter-API), verwenden Sie einen Reverse Proxy, um CORS-Header hinzuzufügen.

Nginx Reverse Proxy

# Nginx as CORS proxy for a third-party API
server {
    listen 80;
    server_name cors-proxy.yoursite.com;

    location /proxy/ {
        # Add CORS headers
        add_header 'Access-Control-Allow-Origin' 'https://yoursite.com' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;

        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' 'https://yoursite.com';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
            add_header 'Access-Control-Max-Age' 86400;
            return 204;
        }

        # Strip /proxy/ prefix and forward to third-party API
        rewrite ^/proxy/(.*) /$1 break;
        proxy_pass https://third-party-api.com;
        proxy_set_header Host third-party-api.com;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

# Usage: fetch('https://cors-proxy.yoursite.com/proxy/endpoint')

Apache Reverse Proxy

# Apache as CORS proxy
<VirtualHost *:80>
    ServerName cors-proxy.yoursite.com

    # Enable required modules
    # a2enmod proxy proxy_http headers rewrite

    <Location "/proxy/">
        Header always set Access-Control-Allow-Origin "https://yoursite.com"
        Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
        Header always set Access-Control-Allow-Headers "Content-Type, Authorization"
        Header always set Access-Control-Allow-Credentials "true"

        # Handle preflight
        RewriteEngine On
        RewriteCond %{REQUEST_METHOD} OPTIONS
        RewriteRule ^(.*)$ $1 [R=204,L]

        # Proxy to backend
        ProxyPass "http://backend:8080/"
        ProxyPassReverse "http://backend:8080/"
    </Location>
</VirtualHost>

Nur-Entwicklung-Lösungen

Diese Lösungen sind nur für die lokale Entwicklung. Niemals in der Produktion verwenden.

Vite Proxy

Vites eingebauter Proxy leitet Anfragen vom Dev-Server an die API weiter und vermeidet CORS komplett:

// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    proxy: {
      // Proxy /api requests to backend
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        // Optional: rewrite path
        // rewrite: (path) => path.replace(/^\/api/, ''),
      },
      // Proxy with WebSocket support
      '/ws': {
        target: 'ws://localhost:8080',
        ws: true,
      },
    },
  },
});

// In your code, use relative URLs:
// fetch('/api/users')  instead of  fetch('http://localhost:8080/api/users')

Webpack devServer Proxy

Create React App und andere webpack-basierte Setups unterstützen einen ähnlichen Proxy:

// package.json (Create React App)
{
  "proxy": "http://localhost:8080"
}

// Or for more control, create src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:8080',
      changeOrigin: true,
    })
  );
};

// webpack.config.js (manual setup)
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
      },
    },
  },
};

Next.js Rewrites

Next.js kann API-Anfragen über seinen eigenen Server mit Rewrites proxyen:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: 'http://localhost:8080/api/:path*',
      },
    ];
  },
};

module.exports = nextConfig;

// In your code:
// fetch('/api/users')  -- proxied to http://localhost:8080/api/users

Häufige Fallstricke und Grenzfälle

Fallstrick 1: Wildcard (*) mit Anmeldedaten

Wenn das Frontend Anmeldedaten sendet, KANN der Server NICHT * verwenden. Er muss den genauen Ursprung zurückgeben:

FALSCH (schlägt mit Anmeldedaten fehl):

// Frontend
fetch('https://api.example.com/data', {
  credentials: 'include'  // Sending cookies
});

// Backend response header:
Access-Control-Allow-Origin: *   // FAILS! Cannot use * with credentials

RICHTIG (dynamischer Ursprung für Anmeldedaten):

// Backend (Express.js example)
app.use((req, res, next) => {
  // Echo back the exact origin
  const origin = req.headers.origin;
  const allowedOrigins = ['http://localhost:3000', 'https://yoursite.com'];

  if (allowedOrigins.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);  // Exact origin
  }
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  next();
});

Fallstrick 2: Fehlender OPTIONS-Handler (Preflight)

Nicht-einfache Anfragen lösen einen Preflight-OPTIONS-Request aus. Wenn der Server 404 oder 405 für OPTIONS zurückgibt, scheitert CORS.

TriggerExample
Custom headersAuthorization, X-Request-ID, X-API-Key
Non-simple methodsPUT, DELETE, PATCH
Non-simple Content-Typeapplication/json, application/xml
ReadableStream bodyStreaming request body
// Ensure your server handles OPTIONS for all API routes:
app.options('*', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,PATCH');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  res.setHeader('Access-Control-Max-Age', '86400');
  res.status(204).end();
});

Fallstrick 3: Cookies und SameSite

Cross-Origin Cookies benötigen drei Bedingungen:

// 1. Frontend: include credentials
fetch('https://api.example.com/data', {
  credentials: 'include'
});

// 2. Backend: allow credentials + exact origin
res.setHeader('Access-Control-Allow-Origin', 'https://yoursite.com');  // NOT *
res.setHeader('Access-Control-Allow-Credentials', 'true');

// 3. Cookie must have correct attributes
Set-Cookie: session=abc123; SameSite=None; Secure; HttpOnly; Path=/

// IMPORTANT: SameSite=None requires Secure (HTTPS).
// This means cross-origin cookies do NOT work on HTTP (localhost).
// For local dev, use a proxy instead of cross-origin cookies.

Fallstrick 4: Preflight-Antworten cachen

Preflight-Anfragen können langsam sein. Verwenden Sie Access-Control-Max-Age zum Cachen:

// Server response header:
Access-Control-Max-Age: 86400  // Cache preflight for 24 hours

// Without this header, the browser sends a preflight OPTIONS
// request before EVERY non-simple request, adding latency.

// With caching, the browser remembers the CORS permissions
// and skips the preflight for subsequent requests.

Hinweis: Chrome begrenzt auf 7200 Sekunden (2 Stunden). Firefox auf 86400 (24 Stunden).

Fallstrick 5: CORS bei Weiterleitungen

Wenn Ihr API-Endpoint weiterleitet (301/302), müssen CORS-Header in BEIDEN Antworten vorhanden sein. Vermeiden Sie Weiterleitungen in API-Endpoints.

// PROBLEM: API endpoint redirects
// GET https://api.example.com/users -> 301 -> https://api.example.com/v2/users
// CORS headers must be on BOTH responses!

// SOLUTION: Avoid redirects in API endpoints
// Option 1: Update the frontend URL
fetch('https://api.example.com/v2/users');  // Use final URL directly

// Option 2: Make the redirect include CORS headers
// (requires server configuration on both the old and new endpoint)

Schnellreferenz: CORS-Header

HeaderZweckBeispielwert
Access-Control-Allow-OriginWhich origins can access the resourcehttps://yoursite.com or *
Access-Control-Allow-MethodsWhich HTTP methods are allowedGET, POST, PUT, DELETE
Access-Control-Allow-HeadersWhich request headers are allowedContent-Type, Authorization
Access-Control-Allow-CredentialsWhether cookies/auth can be senttrue
Access-Control-Max-AgeHow long to cache preflight (seconds)86400
Access-Control-Expose-HeadersWhich response headers JS can readX-Total-Count, X-Request-ID

Häufig gestellte Fragen

Kann man CORS im Browser deaktivieren?

Technisch ja, durch Starten von Chrome mit --disable-web-security, aber das ist extrem gefährlich. Für die Entwicklung verwenden Sie einen Proxy (Vite, webpack).

Warum funktioniert meine API in Postman, aber nicht im Browser?

CORS ist ein reiner Browser-Sicherheitsmechanismus. Postman, curl und Server-zu-Server-Anfragen erzwingen kein CORS.

Ist CORS ein Frontend- oder Backend-Fix?

Fast immer Backend. Der Server muss die richtigen CORS-Header senden. Das Frontend kann CORS nicht umgehen. Die einzige Frontend-Lösung ist ein Proxy.

Sollte man Access-Control-Allow-Origin: * in der Produktion verwenden?

Nur für wirklich öffentliche APIs ohne Cookies oder Authentifizierung. Bei Anmeldedaten müssen genaue Ursprünge angegeben werden.

Was ist eine Preflight-Anfrage?

Eine automatische OPTIONS-Anfrage, die der Browser vor der eigentlichen Anfrage sendet. Tritt bei benutzerdefinierten Headern, nicht-einfachen Methoden oder speziellem Content-Type auf.

Wie behebt man CORS bei WebSocket-Verbindungen?

WebSocket-Verbindungen folgen nicht den CORS-Regeln. Der Server muss den Origin-Header manuell validieren. Bei CORS-Fehlern liegt das Problem meist beim initialen HTTP-Handshake-Endpoint.

𝕏 Twitterin LinkedIn
War das hilfreich?

Bleiben Sie informiert

Wöchentliche Dev-Tipps und neue Tools.

Kein Spam. Jederzeit abbestellbar.

Verwandte Tools ausprobieren

🛡️CSP Header GeneratorNXNginx Config Generator.ht.htaccess Generator4xxHTTP Status Code Reference

Verwandte Artikel

REST API Best Practices: Der komplette Leitfaden für 2026

REST API Design Best Practices: Namenskonventionen, Fehlerbehandlung, Authentifizierung, Paginierung und Sicherheit.

Content Security Policy (CSP) Komplett-Guide: Von den Grundlagen bis zur Produktion

CSP von Grund auf lernen: alle Direktiven, häufige Konfigurationen, Reporting und Deployment.