Direkt zum Inhalt
NikoFischer.com

Main navigation

  • Startseite
  • Über mich
    • My Reading List
    • Recommended Youtube Channels
    • Life Rules
    • Podcast
  • 50-Tage Challenge
  • Impressum
Sprachumschalter
  • German
  • English

Pfadnavigation

  1. Startseite

Supabase Edge Functions CORS Fehler beheben - Vollständige Anleitung 2025

🎸
🚀 Beta läuft

PYNGUP: Rebellion gegen toxische Produktivität

Beta auf 100 Plätze begrenzt. Tasks werden zu sozialen Commitments statt einsamer To-Dos.

🚀 Beta beitreten 📖 Story lesen "€487 verschwendet"

CORS-Fehler gehören zu den frustrierendsten Problemen beim Entwickeln mit Supabase Edge Functions. Sie funktionieren perfekt in Postman oder Insomnia, aber sobald du sie aus dem Browser aufrufst, bekommst du diese kryptische Fehlermeldung:

Access to fetch at 'https://deinprojekt.supabase.co/functions/v1/deine-funktion' 
from origin 'http://localhost:3000' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

In diesem Artikel zeige ich dir genau, wie du CORS in Supabase Edge Functions richtig konfigurierst - mit funktionierenden Code-Beispielen und häufigen Fallstricken.

Das Problem: Warum CORS in Edge Functions anders ist

Anders als bei Supabase's REST API musst du CORS manuell in Edge Functions handhaben. Das liegt daran, dass Edge Functions vollständig anpassbare Server-Funktionen sind, die du selbst kontrollierst.

Wichtig: Supabase stellt keine automatische CORS-Konfiguration für Edge Functions bereit!

Die Lösung: CORS richtig implementieren

Schritt 1: CORS Headers definieren

Erstelle zuerst eine wiederverwendbare CORS-Konfiguration. Ich empfehle, eine _shared/cors.ts Datei anzulegen:

// supabase/functions/_shared/cors.ts
export const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 
    'authorization, x-client-info, apikey, content-type',
  'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',
}

Schritt 2: OPTIONS Request handhaben (KRITISCH!)

Das ist der wichtigste Teil: Der OPTIONS-Check muss ganz oben in deiner Funktion stehen:

// supabase/functions/deine-funktion/index.ts
import { corsHeaders } from '../_shared/cors.ts'

Deno.serve(async (req) => {
  // DIESER CHECK MUSS GANZ OBEN STEHEN!
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }

  // Deine eigentliche Funktionslogik hier
  try {
    const data = await req.json()
    
    // Verarbeitung...
    
    return new Response(
      JSON.stringify({ success: true, data }),
      {
        headers: {
          ...corsHeaders,
          'Content-Type': 'application/json',
        },
      }
    )
  } catch (error) {
    return new Response(
      JSON.stringify({ error: error.message }),
      {
        status: 400,
        headers: {
          ...corsHeaders,
          'Content-Type': 'application/json',
        },
      }
    )
  }
})

Warum der OPTIONS-Check ganz oben stehen muss

Browser senden einen Preflight-Request (OPTIONS) bevor sie den eigentlichen Request ausführen. Dieser Preflight fragt: "Darf ich diese Anfrage stellen?"

Wenn dein OPTIONS-Handler nicht als erstes kommt, könnte deine Funktion einen Fehler werfen, bevor sie überhaupt die CORS-Headers senden kann.

Häufige Fehler und ihre Lösungen

Fehler 1: CORS Headers nur bei erfolgreichen Responses

Falsch:

if (error) {
  return new Response('Error', { status: 500 })
  // Keine CORS Headers!
}

Richtig:

if (error) {
  return new Response('Error', { 
    status: 500,
    headers: corsHeaders  // CORS Headers auch bei Fehlern!
  })
}

Fehler 2: Unvollständige Headers

Stelle sicher, dass deine Headers alle nötigen Werte enthalten:

export const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 
    'authorization, x-client-info, apikey, content-type, x-requested-with',
  'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE, PATCH',
  'Access-Control-Max-Age': '86400', // Cache Preflight für 24h
}

Fehler 3: Credentials mit Wildcard Origin

Sicherheitsproblem:

// NIEMALS in Produktion!
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true'

Besser für Produktion:

const allowedOrigins = [
  'https://deineapp.com',
  'https://www.deineapp.com',
  'http://localhost:3000' // nur für Development
]

const origin = req.headers.get('origin')
const corsOrigin = allowedOrigins.includes(origin) ? origin : 'null'

const corsHeaders = {
  'Access-Control-Allow-Origin': corsOrigin,
  'Access-Control-Allow-Credentials': 'true',
  // ...weitere headers
}

Vollständiges Arbeitsbeispiel

Hier ist eine komplette Edge Function mit korrekter CORS-Behandlung:

// supabase/functions/user-profile/index.ts
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
import { corsHeaders } from '../_shared/cors.ts'

interface ProfileRequest {
  userId: string
  name: string
  email: string
}

Deno.serve(async (req) => {
  // CORS Preflight
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }

  try {
    // Supabase Client initialisieren
    const supabase = createClient(
      Deno.env.get('SUPABASE_URL') ?? '',
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
    )

    // Request Body parsen
    const { userId, name, email }: ProfileRequest = await req.json()

    // Validierung
    if (!userId || !name || !email) {
      return new Response(
        JSON.stringify({ 
          error: 'userId, name und email sind erforderlich' 
        }),
        {
          status: 400,
          headers: { ...corsHeaders, 'Content-Type': 'application/json' }
        }
      )
    }

    // Database Operation
    const { data, error } = await supabase
      .from('profiles')
      .upsert({ user_id: userId, name, email })
      .select()

    if (error) {
      return new Response(
        JSON.stringify({ error: error.message }),
        {
          status: 500,
          headers: { ...corsHeaders, 'Content-Type': 'application/json' }
        }
      )
    }

    // Erfolgreiche Antwort
    return new Response(
      JSON.stringify({ success: true, profile: data[0] }),
      {
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      }
    )

  } catch (error) {
    return new Response(
      JSON.stringify({ error: 'Unbekannter Fehler aufgetreten' }),
      {
        status: 500,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      }
    )
  }
})

Frontend-Integration

So rufst du die Funktion korrekt aus deinem Frontend auf:

// React/Next.js Beispiel
const updateProfile = async (userData) => {
  try {
    const response = await fetch(
      'https://dein-projekt.supabase.co/functions/v1/user-profile',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${supabase.auth.session()?.access_token}`,
          'apikey': 'dein-anon-key'
        },
        body: JSON.stringify(userData)
      }
    )

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    const result = await response.json()
    return result
  } catch (error) {
    console.error('Profile update failed:', error)
    throw error
  }
}

Testing und Debugging

1. Browser Developer Tools nutzen

Öffne die Network-Registerkarte und schaue dir sowohl den OPTIONS- als auch den POST-Request an:

  • OPTIONS Request: Sollte Status 200 mit CORS-Headers zurückgeben
  • Actual Request: Dein eigentlicher API-Call

2. CURL für schnelle Tests

# OPTIONS Request testen
curl -X OPTIONS \
  -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: content-type" \
  https://dein-projekt.supabase.co/functions/v1/deine-funktion

# Erwartete Antwort sollte CORS Headers enthalten

3. Häufige Browser-Spezifische Probleme

Firefox: Manchmal musst du den Browser komplett neustarten, wenn CORS-Probleme auftreten. Firefox kann WebSocket-Verbindungen nicht korrekt aufräumen.

Safari: Besonders streng bei CORS-Policies. Stelle sicher, dass alle Headers exakt korrekt sind.

Lokale Entwicklung

Für lokale Tests mit dem Supabase CLI:

# Edge Functions lokal starten
supabase functions serve --debug

# Deine Funktion ist dann verfügbar unter:
# http://localhost:54321/functions/v1/deine-funktion

Troubleshooting Checklist

Falls du immer noch CORS-Probleme hast, prüfe diese Punkte:

  1. ✅ OPTIONS-Handler steht ganz oben in deiner Funktion
  2. ✅ CORS-Headers sind in ALLEN Responses enthalten (Erfolg und Fehler)
  3. ✅ Alle erforderlichen Headers sind in Access-Control-Allow-Headers aufgelistet
  4. ✅ Dein Frontend sendet den korrekten Content-Type Header
  5. ✅ Du mischst nicht Wildcard Origins mit Credentials in Produktion

Advanced: Dynamische Origin-Behandlung

Für Anwendungen mit mehreren Domains:

const getDynamicCorsHeaders = (request: Request) => {
  const origin = request.headers.get('origin')
  
  // Definiere deine erlaubten Origins
  const allowedOrigins = [
    'https://app.deineseite.com',
    'https://admin.deineseite.com',
    ...(Deno.env.get('ENVIRONMENT') === 'development' 
      ? ['http://localhost:3000', 'http://127.0.0.1:3000'] 
      : [])
  ]
  
  const corsOrigin = allowedOrigins.includes(origin) ? origin : null
  
  return {
    'Access-Control-Allow-Origin': corsOrigin || 'null',
    'Access-Control-Allow-Headers': 
      'authorization, x-client-info, apikey, content-type',
    'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',
    'Access-Control-Allow-Credentials': 'true',
  }
}

// Verwende in deiner Funktion:
Deno.serve(async (req) => {
  const corsHeaders = getDynamicCorsHeaders(req)
  
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }
  
  // ... Rest deiner Funktion
})

Edge Cases und weitere Lösungen

Problem: CORS Fehler trotz korrekter Implementation

Manchmal können auch andere Faktoren CORS-Probleme verursachen:

// Überprüfe auch diese Headers
export const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 
    'authorization, x-client-info, apikey, content-type, x-requested-with, cache-control',
  'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE, PATCH',
  'Access-Control-Max-Age': '86400',
  'Access-Control-Expose-Headers': 'content-length', // Wichtig für manche Clients
}

Development vs Production

Verwende verschiedene CORS-Konfigurationen je nach Umgebung:

const isDevelopment = Deno.env.get('ENVIRONMENT') === 'development'

export const corsHeaders = {
  'Access-Control-Allow-Origin': isDevelopment ? '*' : 'https://deineapp.com',
  'Access-Control-Allow-Headers': 
    'authorization, x-client-info, apikey, content-type',
  'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',
  'Access-Control-Allow-Credentials': isDevelopment ? 'false' : 'true',
}

Fazit

CORS in Supabase Edge Functions ist zunächst verwirrend, aber mit der richtigen Implementierung unproblematisch. Die wichtigsten Punkte:

  1. OPTIONS-Handler muss ganz oben stehen
  2. CORS-Headers bei ALLEN Responses inkludieren
  3. Niemals * mit Credentials in Produktion verwenden
  4. Browser Developer Tools für Debugging nutzen
  5. Verschiedene Konfigurationen für Development und Production

Mit diesen Beispielen sollten deine CORS-Probleme der Vergangenheit angehören. Falls du trotzdem Probleme hast, überprüfe die Reihenfolge deines Codes - der OPTIONS-Check muss wirklich als allererstes kommen!

Weitere Ressourcen

  • Supabase CORS Dokumentation
  • MDN CORS Guide
  • Supabase Edge Functions Beispiele

Hast du weitere CORS-Probleme mit Supabase? Schreib einen Kommentar und ich helfe gerne weiter!

Tags

  • Supabase

Comments

Hilfe zum Textformat

Restricted HTML

  • Erlaubte HTML-Tags: <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Zeilenumbrüche und Absätze werden automatisch erzeugt.
  • Website- und E-Mail-Adressen werden automatisch in Links umgewandelt.

Related articles

Supabase Storage: File Upload richtig implementieren
Binance API Guide: Crypto Trading Bot mit Supabase Backend - Live Trading Data Storage und Monitoring

Über den Autor

Nikolai Fischer ist Gründer von Kommune3 (seit 2007) und führender Experte für die Verbindung von Software-Entwicklung und Unternehmertum. Mit 17+ Jahren Erfahrung hat er hunderte von Projekten geleitet und erreichte #1 auf Hacker News. Als Host des Podcasts "Kommit mich" und Gründer von skillution verbindet er technische Expertise mit unternehmerischem Denken. Seine Artikel über moderne Webentwicklung und systematisches Problem-Solving haben tausende von Entwicklern beeinflusst.

Folge Niko auf:

  • Website: nikofischer.com
  • LinkedIn: Nikolai Fischer
  • Podcast: Kommit mich
Ihre Anmeldung konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.
Ihre Anmeldung war erfolgreich.

Newsletter

Melden Sie sich zu unserem Newsletter an, um auf dem Laufenden zu bleiben.

Nikolai Fischer

✌ Hi, ich bin Niko
Unternehmer, Entwickler & Podcaster

Kontaktier mich:

  • E-Mail
  • Telefon
  • LinkedIn

My Reading List

  • $100M Leads: How to Get Strangers To Want To Buy Your Stuff - Alex Hormozi
  • Quantitative Trading: How to Build Your Own Algorithmic Trading Business (Wiley Trading) - Ernest P. Chan
  • Hands-On Machine Learning for Algorithmic Trading: Design and implement investment strategies based on smart algorithms that learn from data using Python - Stefan Jansen
  • Algorithmic Trading - Ernie Chan
  • Let Me Tell You a Story: Tales Along the Road to Happiness - Jorge Bucay
more
RSS feed