Design wechseln

JWT oder Session? Nach diesem Guide ist die Entscheidung klar

Easton editorial illustration: rendering-mode selector

Der Cursor blinkt zwischen session: { strategy: "jwt" } und session: { strategy: "database" }. Das Release steht an – und Sie fragen sich noch: JWT oder Session?

Bei der Technologieauswahl wirken beide Optionen gut, aber welche passt wirklich zu Ihrem Projekt? JWT ist zustandslos und spart DB-Abfragen, lässt sich aber nicht sofort widerrufen. Session erlaubt sofortige Kontrolle, kostet aber pro Request eine Datenbankabfrage. In diesem Artikel gehen wir die Unterschiede und die Entscheidung durch.

Zuerst: Was sind JWT und Session?

Viele können Definitionen aufsagen – klar erklären ist schwerer. Ein Bild hilft:

Session ist wie eine Mitgliedskarte im Fitnessstudio. Beim Abschluss trägt das Studio Ihre Daten im System. Beim Besuch reicht das Scannen (Session-ID) – die Rezeption sieht Status, Laufzeit und Restkontingent. Alles Wichtige liegt in der Datenbank des Studios.

JWT ist wie ein Ausweis. Name, Geburtsdatum, Adresse stehen auf dem Dokument. Zur Identifikation zeigen Sie den Ausweis – ohne Nachschlagen in einem externen System.

Daraus folgt das Verhalten:

  • Session: Nach dem Login erzeugt der Server eine Session-ID, speichert Nutzerdaten in der DB und sendet die ID per Cookie an den Browser. Jede weitere Anfrage bringt die Session-ID mit – der Server liest die zugehörigen Daten aus der DB.

  • JWT: Nach dem Login packt der Server Nutzerinfos in ein signiertes Token (JWT) und sendet es an den Browser. Bei jeder Anfrage prüft der Server nur die Gültigkeit des Tokens – ohne DB-Zugriff.

0
JWT DB-Abfragen
Folge-Requests ohne DB
jede
Session DB-Abfrage
Jeder Request trifft die DB
15 Min.
empfohlene JWT-Laufzeit
mit Refresh Token
4 KB
Cookie-Größenlimit
JWT darf nicht zu groß werden
Source: Praxiserfahrung

JWT im Detail

Warum JWT so beliebt ist

JWT ist in der Community stark verbreitet – besonders bei Serverless und Microservices. Der Hauptgrund: weniger Datenbankdruck.

Ein Bekannter betreibt einen E-Commerce-Shop auf Vercel mit JWT. Sein Fazit: „Weniger Aufwand“ – keine Sorge um Connection-Limits oder Session-Tabellen-Performance, Edge-Deployment problemlos. Einmal einloggen, JWT mitgeben, Server prüft die Signatur – fertig.

JWT passt besonders zu:

1. Serverless / Edge
Auf Vercel, Cloudflare Workers & Co. ist JWT oft Standard. Funktionen sind zustandslos, Requests landen auf verschiedenen Instanzen. Mit Session bräuchten Sie Redis o. Ä. für gemeinsame Session-Daten. Mit JWT reicht das Token – jede Instanz verarbeitet gleich.

2. Hohe Last
Bei Flash-Sales ist die DB der Engpass. Mit Session: jede Anfrage eine Abfrage. Mit JWT: nur beim Login die DB, danach lokale Validierung – deutlich schneller.

3. Microservices
Bei mehreren Services (User, Order, Payment) teilen sich JWTs die Auth-Infos. Kein zentraler Session-Store, kein Session-Lookup pro Service.

Fallstricke bei JWT

Klingt gut – aber es gibt echte Stolpersteine.

Größtes Problem: kein sofortiger Widerruf

Einmal mussten wir ein kompromittiertes Konto sofort abmelden. Mit Session: DB-Eintrag löschen – erledigt. Mit JWT: peinlich – bis zum Ablauf bleibt das Token gültig.

Blacklist? Geht – aber dann verlieren Sie den Stateless-Vorteil. Jede Anfrage prüft die Liste – ähnlich wie eine Session-Tabelle.

Deshalb: kurze Laufzeit (z. B. 15 Minuten) plus Refresh Token. Gestohlenes Token ist spätestens dann tot.

Cookie-Größenlimit

JWT codiert Nutzerdaten ins Token. Rollen, Rechte, Einstellungen – das Token wächst. Browser-Cookies sind auf etwa 4 KB begrenzt.

Faustregel: nur das Nötigste ins JWT – z. B. User-ID und Ablaufzeit. Rest bei Bedarf nachladen.

NextAuth.js: JWT in der Praxis

Mit Next.js ist NextAuth.js (Auth.js) die verbreitete Lösung. JWT-Konfiguration ist überschaubar:

// auth.ts
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [GitHub],
  session: {
    strategy: "jwt",
    maxAge: 30 * 24 * 60 * 60, // 30 Tage
  },
  callbacks: {
    async jwt({ token, user }) {
      // Beim ersten Login User-Infos ins Token
      if (user) {
        token.id = user.id
        token.role = user.role
      }
      return token
    },
    async session({ session, token }) {
      // Token-Infos an den Client durchreichen
      if (token) {
        session.user.id = token.id
        session.user.role = token.role
      }
      return session
    },
  },
})

Wichtige Punkte:

  1. NEXTAUTH_SECRET setzen (Auth.js v5: AUTH_SECRET). Mit npx auth secret erzeugen. Signiert und verschlüsselt das JWT – nicht leaken.

  2. Rolling Session: Bei Aktivität automatisch verlängern mit updateAge:

session: {
  strategy: "jwt",
  maxAge: 30 * 24 * 60 * 60,
  updateAge: 24 * 60 * 60, // alle 24 Stunden erneuern
}

Bei Aktivität innerhalb von 24 Stunden verlängert sich die Session – kein überraschendes Abmelden.

  1. Edge Runtime (2025): Für Middleware die Konfiguration splitten:
// auth.config.ts – läuft auf Edge
export default {
  providers: [GitHub],
  session: { strategy: "jwt" },
}

// auth.ts – DB-Operationen nur auf Node.js
import { PrismaAdapter } from "@auth/prisma-adapter"
import authConfig from "./auth.config"

export const { handlers, auth } = NextAuth({
  ...authConfig,
  adapter: PrismaAdapter(prisma),
})

Edge Runtime unterstützt nicht alle Node-APIs. Middleware nutzt auth.config.ts, Server-Routen die vollständige auth.ts.

Database Session im Detail

Wann Session Pflicht ist

JWT ist praktisch – aber manche Szenarien verlangen Session.

Ein Backoffice für eine Finanzfirma: bei verdächtigem Login sofort abmelden – harte Anforderung. JWT passt da schlecht.

Database Session eignet sich für:

1. Sofortige Kontrolle
Gerätelimit (nur ein Gerät), Admin-Kick, alle Sessions nach Passwortänderung invalidieren – mit Session trivial, mit JWT viel Zusatzlogik.

2. Hohe Sicherheit
Banken, Medizin, Behörden: jede Anfrage soll den aktuellen Berechtigungsstand aus der DB haben. Session liefert das; JWT kann veraltete Claims tragen.

3. Klassischer Monolith
App und DB auf einem Stack – Session-Lookup ist günstig, Sessions lassen sich zentral steuern.

Performance bei Session

Jede Anfrage trifft die Datenbank – bei hohem Traffic ein Engpass.

Optimierungen:

  1. Redis für Sessions – schneller als klassische DB, mit TTL.

  2. Minimale Session-Daten – nur User-ID, Rest bei Bedarf.

  3. Ablaufzeiten und Aufräumen – sonst wächst die Session-Tabelle endlos.

Next.js: Session-Konfiguration

NextAuth.js nutzt standardmäßig JWT. Für Database Session brauchen Sie einen Adapter:

// auth.ts
import NextAuth from "next-auth"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { PrismaClient } from "@prisma/client"
import GitHub from "next-auth/providers/github"

const prisma = new PrismaClient()

export const { handlers, auth } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [GitHub],
  session: {
    strategy: "database",
    maxAge: 30 * 24 * 60 * 60, // 30 Tage
    updateAge: 24 * 60 * 60, // täglich aktualisieren
  },
})

Prisma legt die Session-Tabelle an – npx prisma db push:

// schema.prisma
model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
  id       String    @id @default(cuid())
  email    String    @unique
  sessions Session[]
}

Performance-Tipps:

  1. Index auf sessionToken und expires.

  2. Abgelaufene Sessions regelmäßig löschen:

// cleanup-sessions.ts
async function cleanupExpiredSessions() {
  await prisma.session.deleteMany({
    where: {
      expires: {
        lt: new Date(),
      },
    },
  })
}

// täglich um 3 Uhr

Entscheidungsrahmen aus der Praxis

Theorie reicht nicht – hier ein Framework für Ihr Projekt:

Nach Projekttyp

ProjekttypEmpfehlungBegründung
ServerlessJWTZustandslos, kein gemeinsamer Session-Store
MonolithSessionDB nah, Lookup günstig
MicroservicesJWT oder HybridAuth über Services hinweg
Content/BlogJWTEinfache Auth, JWT reicht
SaaS-BackofficeSessionFeine Rechte- und Session-Kontrolle

Nach Sicherheitsniveau

  • Niedrig (Blog, Forum): JWT, z. B. 30 Tage Ablauf
  • Mittel (E-Commerce, Social): JWT + kurze Laufzeit + Refresh Token
  • Hoch (Finanzen, Medizin, Admin): Session + kurze Laufzeit + Rolling Session

Fallstudien

Fall 1: E-Commerce – JWT

Der Shop auf Vercel wählte JWT:

  • Serverless passt zu JWT
  • Weniger DB-Last bei vielen Nutzern
  • Session-Kontrolle weniger kritisch; langsames Token-Ablauf nach Logout akzeptabel

Kompromiss: JWT-Laufzeit 7 statt 30 Tage – gestohlene Tokens verlieren schneller an Wert.

Fall 2: OA-System – Session

Internes OA mit Session:

  • Rollenänderungen sofort wirksam
  • Limit gleichzeitiger Geräte
  • Intranet – DB-Latenz vernachlässigbar

Session mit Redis: Antwortzeiten unter 50 ms.

Fall 3: Hybrid – wie Clerk

Viele Auth-Plattformen (Clerk, Auth0) mischen:

  • Kurzlebiger Token (JWT, ~15 Min.) für API-Aufrufe
  • Refresh Token in der DB
  • Session-Records für Widerruf aus der Ferne

Performance von JWT, Kontrolle von Session – aufwendiger in der Eigenimplementierung.

Unsere Empfehlung

  1. Standard: JWT – für die meisten Projekte ausreichend, einfach, performant.

  2. Session, wenn:

    • Gerätelimit nötig
    • sofortiger Kick erforderlich
    • Finanzen/Medizin o. Ä.
    • alle Logins nach Passwortänderung sofort ungültig
  3. Kein Over-Engineering – kleines Projekt: einfache Lösung zuerst. Hybrid am Anfang bläht oft die Komplexität ohne echten Nutzen.

Ablauf und UX: Best Practices

Die gewählte Strategie muss Ablauf sauber handhaben – sonst leidet die UX.

JWT-Ablauf

Klassischer Frust: Formular ausfüllen, Submit – „Session abgelaufen“, Inhalt weg.

Lösung: Refresh Token + stilles Refresh

  1. Zwei Tokens:

    • Access Token: ~15 Minuten für API
    • Refresh Token: ~30 Tage für neues Access Token
  2. Frontend erneuert Access Token kurz vor Ablauf (z. B. bei 2 Minuten Rest)

  3. Nutzer merkt nichts

NextAuth.js unterstützt das Muster – Beispiel:

callbacks: {
  async jwt({ token, user, account }) {
    if (account && user) {
      // Erster Login
      return {
        ...token,
        accessToken: account.access_token,
        accessTokenExpires: Date.now() + account.expires_in * 1000,
        refreshToken: account.refresh_token,
      }
    }

    // Access Token noch gültig
    if (Date.now() < token.accessTokenExpires) {
      return token
    }

    // Abgelaufen – Refresh
    return refreshAccessToken(token)
  },
}

async function refreshAccessToken(token) {
  try {
    const response = await fetch("https://api.example.com/oauth/token", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams({
        client_id: process.env.OAUTH_CLIENT_ID,
        grant_type: "refresh_token",
        refresh_token: token.refreshToken,
      }),
    })

    const refreshedTokens = await response.json()

    return {
      ...token,
      accessToken: refreshedTokens.access_token,
      accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000,
      refreshToken: refreshedTokens.refresh_token ?? token.refreshToken,
    }
  } catch (error) {
    return {
      ...token,
      error: "RefreshAccessTokenError",
    }
  }
}

Session-Ablauf

Rolling Session vs. absolutes Timeout

  • Rolling: Aktivität verlängert die Session – Standard für die meisten Apps
  • Absolut: Festes Ende unabhängig von Aktivität – z. B. Banking

NextAuth.js:

session: {
  strategy: "database",
  maxAge: 2 * 60 * 60, // 2 Stunden absolut
  updateAge: 30 * 60, // bei Aktivität innerhalb 30 Min. verlängern
}

Aktivität innerhalb von 30 Minuten verlängert; spätestens nach 2 Stunden ist Schluss.

Idle-Timeout

„15 Minuten ohne Aktion abmelden“ – z. B. im Frontend:

let idleTimer
function resetIdleTimer() {
  clearTimeout(idleTimer)
  idleTimer = setTimeout(() => {
    // 15 Minuten idle → abmelden
    signOut()
  }, 15 * 60 * 1000)
}

// Nutzeraktivität
document.addEventListener("mousemove", resetIdleTimer)
document.addEventListener("keypress", resetIdleTimer)

Hybrid im Mainstream

Teams kombinieren:

  • JWT für API (schnell)
  • aktive Sessions in der DB (kontrollierbar)

Clerk, Auth0 & Co. folgen diesem Muster – oder den Dienst direkt nutzen.

Edge Runtime

Next.js-Middleware auf Edge begünstigt JWT. Schutz ganzer Bereiche (z. B. /dashboard) in der Middleware ist mit JWT oft einfacher.

Passkey und WebAuthn

Passkeys (WebAuthn) – Login per Fingerabdruck/Face ID ohne Passwort – werden häufiger. NextAuth.js v5 unterstützt Passkey; Auth-Flows werden reicher, UX oft besser.

Abschluss

Sie sollten JWT und Session nun klarer einordnen können. Es gibt keine Universalantwort – Stärken verstehen und zum Projekt passen.

Unsere Erfahrung: Für die meisten Projekte reicht JWT; bei Bedarf an sofortiger Kontrolle Session. Nicht zu früh verkomplizieren – einfache Lösungen sind oft die zuverlässigsten.

Noch unsicher? Starten Sie mit NextAuth.js-Default (JWT), messen Sie in Produktion. Wechsel zu Session ist meist wenige Zeilen Konfiguration.

Wenn Sie um drei Uhr morgens noch zwischen jwt und database hängen: eine Nacht Schlaf hilft oft mehr als eine weitere Stunde Docs. Und wenn Ihr Szenario hier fehlt – gerne in den Kommenten diskutieren; gemeinsam findet man meist die passende Variante.

FAQ

Was ist der Kernunterschied zwischen JWT und Session?
JWT ist zustandslos: Nutzerinfos stecken im Token, der Server prüft nur die Signatur – keine DB. Session ist zustandsbehaftet: Infos liegen in der DB, jede Anfrage validiert dort. JWT passt zu Serverless/hoher Last, Session zu Szenarien mit sofortiger Kontrolle.
Wann JWT, wann Session?
JWT: Serverless/Edge, hohe Last, Microservices, Content-Sites/Blogs. Session: sofortige Kontrolle (Gerätelimit, Kick), hohe Sicherheit (Finanzen/Medizin), klassischer Monolith. Standard: JWT; bei Kontrollbedarf Session.
JWT lässt sich nicht sofort widerrufen – was tun?
Ein ausgestelltes JWT gilt bis zum Ablauf. Lösungen: 1) kurze Laufzeit (15 Min.) + Refresh Token, 2) Blacklist (verliert Stateless-Vorteil), 3) Session oder Hybrid. Für die meisten Fälle reichen kurze Laufzeit + Refresh Token.
Wie optimiert man Session-Performance?
Jede Anfrage trifft die DB – Engpass bei hohem Traffic. Optimierung: 1) Redis für Sessions, 2) minimal speichern (nur User-ID), 3) Index auf sessionToken und expires, 4) abgelaufene Sessions regelmäßig löschen.
Wie konfiguriert man JWT und Session in NextAuth.js?
JWT: session: { strategy: „jwt“, maxAge: 30 Tage }, Nutzerinfos in callbacks.jwt. Session: PrismaAdapter, session: { strategy: „database“ }, Session-Tabelle nötig. Edge Runtime erfordert getrennte Konfiguration.
Wie handhabt man Ablauf und UX?
JWT: Access Token (15 Min.) + Refresh Token (30 Tage) für stilles Refresh vor Ablauf. Session: Rolling Session (updateAge) bei Aktivität. Vermeiden, dass Nutzer beim Ausfüllen von Formularen plötzlich abgemeldet werden.
Welche Trends 2025 für JWT und Session?
Hybrid im Kommen: JWT für API (schnell) + Session in DB (kontrollierbar). Edge Runtime bevorzugt JWT für Middleware-Auth. Passkey/WebAuthn verbreitet sich, NextAuth.js v5 unterstützt es. Pragmatisch wählen, nicht over-engineeren.

8 Min. Lesezeit · Veröffentlicht am: 19. Dez. 2025 · Aktualisiert am: 14. Juli 2026

Ähnliche Beiträge

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog