Zum Inhalt springen
Infrastruktur & Technologie

Next.js über Supabase sicher betreiben

DevOps-Runbook für Next.js auf Supabase: Architektur, Middleware, Auth-Patterns, Rate Limiting und Claude-Code-Integration.

Mansoor Ahmed
Mansoor Ahmed
Head of Engineering 14 Min. Lesezeit

Nachdem Supabase als Backend-Plattform stabil läuft, entsteht der eigentliche Anwendungs-Stack darüber.

In vielen modernen Projekten übernimmt Next.js die Rolle der App-Schicht:

  • Frontend Rendering
  • Server Side Rendering
  • Server Actions
  • Route Handlers
  • API Proxy
  • Session Handling

Damit wird Next.js faktisch ein Backend-Gateway zwischen Browser und Supabase.

Der häufigste Fehler besteht darin, dass diese Schicht als “Frontend” behandelt wird, obwohl sie tatsächlich serverseitige Logik mit hohen Privilegien enthält.

Dieses Runbook beschreibt, wie Next.js sicher über einer self-hosted Supabase-Plattform betrieben wird.

Jeder Abschnitt enthält:

  • Implementierung
  • prüfbare Bedingung
  • Failure-Scenario

Auf einen Blick - Artikel 2 von 6 der DevOps-Runbook-Serie

  • Next.js als eigener Container, physisch getrennt von Supabase
  • service_role Key nur in einer einzigen Datei erlaubt (lib/supabase/admin.ts)
  • Middleware mit getUser() auf jedem Request (nicht getSession())
  • Ownership Checks in allen Server Actions vor Datenänderungen
  • Rate Limiting mit Upstash Redis auf allen Auth-Endpunkten

Serien-Inhaltsverzeichnis

Diese Anleitung ist Teil unserer DevOps-Runbook-Serie für self-hosted App-Stacks.

  1. Supabase Self-Hosting Runbook
  2. Next.js über Supabase sicher betreiben - dieser Artikel
  3. Supabase Edge Functions sicher einsetzen
  4. Trigger.dev Background Jobs sicher betreiben
  5. Claude Code als Sicherheitskontrolle im DevOps-Workflow
  6. Security Baseline für den gesamten Stack

Artikel 1 beschreibt die Plattform-Basis mit allen Services und deren Sicherheitskonfiguration. Dieser Artikel beschreibt die App-Schicht darüber.

Architekturüberblick

Browser (Client)
     |
     | HTTPS
     |
Next.js App Layer
     |
     +-- @supabase/ssr
     |
     +-- service_role client
           (nur isolierte Admin-Kontexte)
     |
Supabase Platform Layer
     |
     +-- Kong API Gateway
     +-- GoTrue Auth
     +-- PostgREST API
     +-- Realtime
     |
PostgreSQL Data Layer
     |
     +-- Row Level Security

Grundregeln:

Browser    -> spricht nur mit Next.js
Next.js    -> spricht mit Supabase
Supabase   -> kontrolliert Zugriff über RLS

Wenn der Browser mehrere Backend-Services direkt anspricht, entstehen unkontrollierbare Sicherheitsgrenzen.

Teil A - Architekturentscheidungen

Diese Entscheidungen werden selten geändert und bilden das Fundament.

A1 - Next.js als eigenen Service betreiben

Umsetzung

Next.js läuft als eigener Container.

services

nextjs-app
supabase-stack
postgres

Beispiel docker-compose:

services:

  nextjs-app:
    build: ./app
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_SUPABASE_URL=http://kong:8000
      - NEXT_PUBLIC_SUPABASE_ANON_KEY=${ANON_KEY}
      - SUPABASE_SERVICE_ROLE_KEY=${SERVICE_ROLE_KEY}
    networks:
      - internal

Next.js darf nicht innerhalb des Supabase-Stacks laufen. Eine Analyse von über 500 Next.js-Deployments zeigt, dass 23% aller produktiven Next.js-Instanzen mindestens eine serverseitige Umgebungsvariable im Client-Bundle exponieren (Snyk State of Open Source Security 2024).

Prüfbare Bedingung

docker ps --format '{{.Names}}'

Erwartung:

nextjs-app
supabase-kong
supabase-postgres
supabase-auth

Failure Scenario

Wenn Next.js im selben Container wie Supabase läuft:

  • Prozessraum wird geteilt
  • Secrets liegen im selben Environment
  • ein kompromittierter Next.js Server hat direkten Zugriff auf alle Backend-Services

A2 - Browser spricht nur mit Next.js

Umsetzung

Der Browser darf nur eine öffentliche URL sehen.

https://app.example.com

Nicht erlaubt:

https://app.example.com:8000
https://app.example.com:5432
https://app.example.com:9000

Firewall-Beispiel:

iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 8000 -j DROP
iptables -A INPUT -p tcp --dport 5432 -j DROP
iptables -A INPUT -p tcp --dport 9000 -j DROP

Prüfbare Bedingung

nmap -p 443,3000,5432,8000,9000 app.example.com

Erwartung:

443 open
alle anderen filtered

Failure Scenario

Wenn Supabase Studio öffentlich erreichbar ist:

  • vollständiger Datenbankzugriff
  • Schema-Manipulation
  • Zugriff auf Storage Buckets

A3 - Security Headers setzen

Next.js fungiert als Gateway und muss HTTP-Security-Header setzen.

Umsetzung

next.config.js
const securityHeaders = [
  { key: "X-Frame-Options", value: "DENY" },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  { key: "Permissions-Policy", value: "camera=(), microphone=()" },
  { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains" },
  {
    key: "Content-Security-Policy",
    value: "default-src 'self'; img-src 'self' data: blob:; frame-ancestors 'none'"
  }
]

Prüfbare Bedingung

curl -I https://app.example.com

Erwartete Header:

X-Frame-Options
Content-Security-Policy
Strict-Transport-Security

Failure Scenario

Ohne CSP:

  • XSS-Angriffe laden externe Skripte
  • Token können exfiltriert werden

Teil B - Implementierungschecks

Diese Regeln gelten für jede Codeänderung.

B1 - Environment Variablen trennen

Übersicht der Umgebungsvariablen

VariableSichtbarkeitErlaubter OrtRisiko bei Leak
NEXT_PUBLIC_SUPABASE_URLClient.env, CodeGering (öffentlich)
NEXT_PUBLIC_SUPABASE_ANON_KEYClient.env, CodeMittel (RLS schützt)
SUPABASE_SERVICE_ROLE_KEYServer-only.env, lib/supabase/admin.tsKritisch (umgeht RLS)
DATABASE_URLServer-only.envKritisch (voller DB-Zugang)
TRIGGER_API_KEYServer-only.envHoch (Job-Ausführung)

Client-sichtbar:

NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY

Server-only:

SUPABASE_SERVICE_ROLE_KEY
DATABASE_URL
TRIGGER_API_KEY

Prüfbare Bedingung

grep -r "NEXT_PUBLIC_" .env*

Secrets dürfen dort nicht vorkommen.

Build Leak Check

grep -r "SERVICE_ROLE" .next/

Erwartung:

keine Treffer

Failure Scenario

service_role im Client-Bundle bedeutet:

  • vollständiger DB-Zugriff
  • RLS komplett wirkungslos

Wer die Grundlagen der Secret-Verwaltung kennt, versteht, warum diese Trennung essenziell ist.

B2 - Supabase SSR Client korrekt konfigurieren

Server-Client:

import { createServerClient } from "@supabase/ssr"

Server verwendet anon key, nicht service_role.

Admin-Client:

createClient(url, SERVICE_ROLE_KEY)

Nur für administrative Aufgaben.

Prüfbare Bedingung

grep -rn "SERVICE_ROLE" app/

Erwartung: nur in

lib/supabase/admin.ts

Failure Scenario

Server-Client mit service_role:

  • RLS wird komplett umgangen
  • alle Requests haben Adminrechte

B3 - Middleware für Auth und Token Refresh

Middleware läuft vor jedem Request.

middleware.ts

Umsetzung

const { data: { user } } = await supabase.auth.getUser()

Nicht:

getSession()

Prüfbare Bedingung

grep getUser middleware.ts

Failure Scenario

Ohne Middleware:

  • Token Refresh funktioniert nicht
  • Session bricht nach 1h ab

B4 - Mutation Pattern

Alle Mutationen folgen dem gleichen Ablauf.

Auth
Input Validation
Ownership Check
Mutation
Logging

Beispiel

const { user } = await supabase.auth.getUser()

if (!user) return

const post = await supabase
  .from("posts")
  .select("user_id")
  .eq("id", postId)

if (post.user_id !== user.id)
  throw new Error("forbidden")

Failure Scenario

Ohne Ownership Check:

deletePost(id)

Ein Nutzer kann fremde Daten löschen.

B5 - Rate Limiting

Next.js hat kein integriertes Rate Limiting.

Empfohlene Lösung:

Upstash Redis

Beispiel

10 requests / minute / IP

Prüfbare Bedingung

for i in {1..20}
do curl -X POST /api/login
done

Erwartung:

HTTP 429

B6 - Logging ohne Secrets

Logs dürfen nicht enthalten:

JWT Tokens
service_role keys
emails
passwords

Prüfbare Bedingung

grep console.log app/

Teil C - Betrieb

C1 - Dependency Updates

Next.js Releases sind häufig.

Wöchentlich prüfen:

npm audit
npm outdated

Empfohlen:

Renovate / Dependabot

C2 - Claude Code Integration

Architektur:

Git Push
   |
Deterministische Checks
   |
Security Report
   |
Claude Analyse
   |
DevOps Entscheidung

Deterministische Checks

grep service_role
grep NEXT_PUBLIC
npm audit

Claude Analyse

Claude prüft:

  • neue Server Actions
  • neue API Routes
  • Ownership Patterns
  • Input Validation
  • Architektur-Drift

Claude führt keine Änderungen auf Production aus.

Deployment-Checkliste

Vor jedem Deployment prüfen:

[ ] Next.js läuft als eigener Service
[ ] Supabase Ports extern geschlossen
[ ] Security Headers aktiv

[ ] kein service_role im Client Bundle
[ ] service_role nur im Admin Client

[ ] middleware.ts vorhanden
[ ] Server Actions prüfen Auth
[ ] Ownership Checks implementiert

[ ] Login Rate Limit aktiv

[ ] npm audit ohne critical findings

Fazit

Next.js ist im modernen Stack kein Frontend, sondern eine privilegierte Server-Schicht.

Sicherheit entsteht durch drei Ebenen:

Architektur
Implementierungschecks
laufende Audits

Die Kombination aus CI Security Checks und Claude Code Analyse erkennt sowohl bekannte Muster als auch neue Risiken.

Wer diese Prinzipien zusammen mit einer Cert-Ready-by-Design-Architektur verfolgt, baut prüfbare Sicherheit statt nachträglicher Audits.

Audit-Checkliste als Download

Vorbereiteter Prompt für Claude Code. Laden Sie die Datei auf Ihren Server und starten Sie Claude Code im Projektverzeichnis Ihrer Next.js-Anwendung. Claude Code prüft automatisch alle Sicherheitspunkte aus diesem Runbook und meldet BESTANDEN, WARNUNG oder KRITISCH.

claude -p "$(cat claude-check-artikel-2-nextjs.md)" --allowedTools Read,Grep,Glob,Bash

Checkliste herunterladen

Serien-Inhaltsverzeichnis

  1. Supabase Self-Hosting Runbook
  2. Next.js über Supabase sicher betreiben - dieser Artikel
  3. Supabase Edge Functions sicher einsetzen
  4. Trigger.dev Background Jobs sicher betreiben
  5. Claude Code als Sicherheitskontrolle im DevOps-Workflow
  6. Security Baseline für den gesamten Stack

Der nächste Artikel beschreibt, wie Supabase Edge Functions als Integrationspunkte sicher eingesetzt werden - ohne eine zweite Backend-Architektur zu bauen.

Bert Gogolin

Bert Gogolin

Geschäftsführer, Gosign

AI Governance Briefing

Enterprise AI, Regulierung und Infrastruktur - einmal im Monat, direkt von mir.

Kein Spam. Jederzeit abbestellbar. Datenschutzerklärung

Next.js Supabase Security DevOps Self-Hosting
Artikel teilen

Häufige Fragen

Warum darf Next.js nicht im selben Container wie Supabase laufen?

Wenn Next.js im selben Container wie Supabase läuft, wird der Prozessraum geteilt und Secrets liegen im selben Environment. Ein kompromittierter Next.js Server hat dann direkten Zugriff auf alle Backend-Services.

Warum darf der service_role Key nicht im Client-Code verwendet werden?

Der service_role Key umgeht alle Row Level Security Policies. Wenn er im Client-Bundle landet, hat jeder Nutzer vollen Datenbankzugriff und RLS wird komplett wirkungslos.

Was passiert wenn eine Server Action keinen Auth Check hat?

Ohne getUser()-Check in einer Server Action kann jeder Nutzer mit einem gültigen Session-Cookie die Action aufrufen, auch für Daten anderer Nutzer. Da Server Actions über HTTP POST erreichbar sind, reicht ein einfacher curl-Befehl mit einem gestohlenen Cookie.