Next.js-Entwickler in Bonn

Next.js Entwickler für Organisationen und Unternehmen in Bonn: warum jede Server Action ein öffentlicher Endpunkt ist, was Next.js selbst schützt und welche Prüfungen in jede Action gehören. Remote und schriftlich, erste Einschätzung kostenlos.

Next.js-Entwickler

Mein Platz in der WordPress-Community

  • Mein wordpress.org-Konto besteht seit 2006.
  • Ich habe zum WordPress-Core in den Versionen 7.0 und 7.1 beigetragen.
  • Ich betreue 30 Plugins auf wordpress.org.
  • Ich organisiere WordCamps und trete dort als Speaker auf.
  • Ich war bei jedem WordCamp Europe von 2017 bis 2026 dabei.

Mein WordPress.org-Profil

#Next.js in Bonn: Formulare, die jeder per POST erreicht

Die Bundesstadt Bonn beschreibt sich als deutsche Stadt der Vereinten Nationen. Mit dem Klimasekretariat UNFCCC und weiteren UN-Einrichtungen kamen viele international arbeitende Organisationen in die Stadt, dazu nennt die Stadt rund 150 Nichtregierungsorganisationen (bonn.de, UNO und Internationales). Wer für einen Verband, eine Stiftung oder ein Unternehmen im Bundesviertel ein Portal baut, landet schnell bei Formularen mit echten Daten: Anmeldungen zu Veranstaltungen, Mitgliederbereiche, Bewerbungen, Änderungen an einem Profil.

In Next.js laufen solche Formulare heute meist über Server Actions. Das ist bequem, denn das Formular ruft direkt eine Funktion auf dem Server auf. Genau hier liegt aber ein Missverständnis, das ich in Code-Durchsichten oft sehe: Die Funktion wirkt wie ein interner Baustein der Seite, ist aber ein Endpunkt, den jeder mit dem passenden POST-Request aufrufen kann.

Ich bin Mariusz Szatkowski, ein einzelner Entwickler in Gdynia, und arbeite seit 2006 mit WordPress, heute auch mit Next.js und Headless-Architekturen. Für Bonner Auftraggeber arbeite ich remote und schriftlich, ohne Termine vor Ort. Gestaltung, Entwürfe und Wireframes kommen von Ihnen, ich setze sie um. Diese Seite zeigt, wie ich Server Actions absichere, und enthält eine Prüfliste, die Sie auch ohne mich verwenden können.

Hochhaus Langer Eugen in Bonn

Foto: Dietmar Rabich, CC BY-SA 4.0, Wikimedia Commons.

#Was eine Server Action technisch ist

Die Anleitung Server Actions and Mutations beschreibt den Mechanismus nüchtern. Beim Build ersetzt der Compiler die Funktion im Browser-Bundle durch eine Referenz, also eine Action-ID und einen Dispatcher, der per POST an den Server zurückschickt. Der Code bleibt auf dem Server. Erreichbar ist die Action trotzdem für jeden, der denselben POST senden kann. Die Dokumentation zieht daraus den Schluss, jede Action als nicht vertrauenswürdigen Einstiegspunkt zu behandeln.

Die Anleitung Data Security geht noch einen Schritt weiter: Eine exportierte Server Action ist auch dann von außen aufrufbar, wenn sie nirgends im eigenen Code importiert wird. Next.js entfernt zwar unbenutzte Actions beim Build aus dem Client-Bundle, aber darauf baue ich keine Sicherheit. Eine Action, die es gibt, wird geprüft, als würde sie morgen jemand gezielt aufrufen.

#Was Next.js von sich aus schützt

Das Framework bringt einige Schutzmechanismen mit. Die Dokumentation nennt sie einzeln, und jeder hat eine Grenze, die man kennen sollte.

SchutzWas er leistetWo er aufhört
Nur POSTActions lassen sich nur per POST aufrufen; zusammen mit SameSite-Cookies verhindert das die meisten CSRF-AngriffeSchützt nicht vor einem Angreifer mit eigener gültiger Sitzung
Origin-VergleichDer Host aus dem Origin-Header muss zum Host der Anwendung passen, sonst bricht Next.js abEine Anfrage ganz ohne Origin-Header wird laut Konfigurationsreferenz mit Warnung durchgelassen
Verschlüsselte Action-IDsIDs sind nicht vorhersagbar und werden zwischen Builds neu berechnetEine bekannte ID ist kein Geheimnis, sie steht im Browser-Bundle
Body-GrenzeStandardmäßig 1 MB je AnfrageBegrenzt die Größe, nicht die Zahl der Anfragen
Verschlüsselte ClosuresMitgenommene Variablen reisen verschlüsselt zum BrowserDie Dokumentation rät, sich nicht allein darauf zu verlassen

Der zweite Punkt verdient einen eigenen Satz, weil er in vielen Zusammenfassungen fehlt. Der Origin-Vergleich ist ein Schutz gegen fremde Webseiten, die den Browser Ihrer Nutzer missbrauchen. Gegen ein Skript, das Anfragen direkt baut und den Header einfach weglässt, hilft er nicht. Die eigentliche Prüfung muss deshalb in der Action stehen.

#allowedOrigins hinter Proxy und CDN

Viele Bonner Organisationen betreiben ihre Anwendungen hinter einem Reverse Proxy oder einem CDN. Solange der Proxy den öffentlichen Host in x-forwarded-host weitergibt, braucht es laut Dokumentation keinen Eintrag. Reicht er stattdessen seinen eigenen Host durch, sieht der Server etwa localhost:3000, während der Browser den öffentlichen Namen sendet. Dann gehört der Host aus der Adresszeile in die Liste, nicht der interne.

// next.config.js
module.exports = {
  experimental: {
    serverActions: {
      allowedOrigins: ['portal.example.org', '**.example.org'],
    },
  },
}

Die Regeln für Platzhalter sind enger, als man vermutet. Ein * steht für genau einen Teil des Hostnamens, ** für einen oder mehrere und ist nur am Anfang erlaubt. *.example.org trifft example.org selbst nicht, deshalb stehen in der Liste meist zwei Einträge. Teilersetzungen wie app-*.example.org funktionieren nicht, und ein Port lässt sich nicht per Platzhalter angeben. Die Prüfung läuft in der Entwicklung wie in Produktion. Eine zu großzügige Liste schwächt den Schutz, deshalb trage ich nur Hosts ein, die Sie tatsächlich betreiben.

#Anmeldung und Berechtigung in jeder Action

Der häufigste Fehler in Next.js-Projekten, die ich durchsehe: Die Seite prüft den Login, die Action darin nicht. Die Dokumentation zeigt genau diesen Fall und hält fest, dass eine Weiterleitung auf Seitenebene nur steuert, welche Oberfläche gerendert wird. Die Action ist ein eigener Einstiegspunkt und muss die anfragende Person selbst prüfen.

Danach kommt die zweite Frage: Darf diese Person diesen Datensatz ändern? Die Anleitung empfiehlt ein Muster, das ich in jedem Projekt verwende. Der Browser schickt nur eine Referenz, meist eine ID, und die gewünschte Änderung. Alles andere, vor allem der Besitz, wird aus der Sitzung und der Datenbank neu gelesen.

'use server'

import { z } from 'zod'
import { auth } from '@/lib/auth'
import { db } from '@/lib/db'

const Eingabe = z.object({
  anmeldungId: z.string().uuid(),
  personen: z.coerce.number().int().min(1).max(4),
})

export async function anmeldungAendern(prevState: unknown, formData: FormData) {
  const session = await auth()
  if (!session?.user) return { fehler: 'Bitte melden Sie sich an.' }

  const geprueft = Eingabe.safeParse({
    anmeldungId: formData.get('anmeldungId'),
    personen: formData.get('personen'),
  })
  if (!geprueft.success) return { fehler: 'Bitte prüfen Sie Ihre Angaben.' }

  const anmeldung = await db.anmeldung.findFirst({
    where: { id: geprueft.data.anmeldungId, userId: session.user.id },
  })
  if (!anmeldung) return { fehler: 'Anmeldung nicht gefunden.' }

  await db.anmeldung.update({
    where: { id: anmeldung.id },
    data: { personen: geprueft.data.personen },
  })
  return { ok: true }
}

Drei Dinge sind an diesem Beispiel wichtig. Die Sitzung wird in der Action geholt, nicht von der Seite übernommen. Die Abfrage sucht den Datensatz mit ID und Nutzer-ID zugleich, sodass eine fremde ID einfach nichts findet. Und die Rückgabe enthält nur einen Status, keinen Datenbankdatensatz, denn Rückgabewerte werden serialisiert und an den Browser geschickt. Der erste Parameter prevState ist nötig, weil das Formular die Action über useActionState aufruft und so die Fehlermeldung anzeigen kann (Forms).

Für Löschungen und andere endgültige Vorgänge schlägt die Dokumentation strengere Prüfungen vor, etwa eine frische Anmeldung, und ein lautes Scheitern, wenn die Prüfung nicht greift. In einem Mitgliederbereich heißt das bei mir: Wer sein Konto löschen will, gibt das Passwort erneut ein.

#Eingaben prüfen: die Form ist nicht der Besitz

Die Anleitung zu Server Actions formuliert einen Satz, den ich jedem Team ans Herz lege: Eine Schema-Prüfung mit Zod oder ähnlichen Bibliotheken prüft nur die Form der Eingabe. Ein sauber geformtes Objekt kann trotzdem auf eine Zeile zeigen, die der anfragenden Person nicht gehört. Validierung und Berechtigung sind zwei getrennte Schritte, und beide gehören in die Action oder in eine Datenzugriffsschicht dahinter.

Als Eingabe zählt dabei alles, was vom Client kommt: FormData, Suchparameter, Header und die Werte in Ordnern mit eckigen Klammern wie [id]. Die Prüfliste der Dokumentation nennt diese Ordner ausdrücklich, weil ihr Inhalt aus der Adresse stammt und frei änderbar ist. HTML-Attribute wie required helfen dem Nutzer beim Ausfüllen, schützen aber den Server nicht.

#Closures, Geheimnisse und Quelltext

Eine Action, die direkt in einer Komponente steht, kann Variablen aus deren Umgebung mitnehmen. Die Dokumentation zeigt das an einem Versionsstand, der beim Rendern festgehalten und beim Absenden verglichen wird. Damit das funktioniert, reisen die Variablen zum Browser und zurück. Next.js verschlüsselt sie und erzeugt bei jedem Build einen neuen Schlüssel je Action. Trotzdem rät die Anleitung, sich nicht allein auf diese Verschlüsselung zu verlassen. Ich halte es einfach: In eine Closure kommt kein Geheimnis und keine personenbezogene Angabe, die der Browser nicht ohnehin sehen darf.

Für zusätzliche Sicherheit nennt die Dokumentation die Taint-APIs von React, die über experimental.taint aktiviert werden. Damit lässt sich ein Objekt oder ein einzelner Wert markieren, sodass React ihn nicht an Client-Komponenten weitergibt. Die Anleitung bezeichnet das als zusätzliche Schicht; das Filtern der Daten in der Datenzugriffsschicht ersetzt es nicht.

Wie wichtig der Ort von Geheimnissen ist, zeigte der Dezember 2025. Unter CVE-2025-55183 konnte eine präparierte Anfrage eine Server Function dazu bringen, den kompilierten Quelltext anderer Server Functions zurückzugeben. Laut Next.js-Team konnten dabei auch Geheimnisse sichtbar werden, wenn sie direkt im Code standen statt zur Laufzeit aus Umgebungsvariablen gelesen zu werden. API-Schlüssel für CRM, Newsletter oder Zahlungsanbieter stehen bei mir deshalb nie im Quelltext, und nur die Datenzugriffsschicht liest process.env.

#Warum proxy.ts allein nicht reicht

Seit Next.js 16 heißt die frühere Middleware proxy.ts. Die Anleitung zur Authentifizierung beschreibt sie als Ort für optimistische Prüfungen: Sie liest die Sitzung aus dem Cookie und leitet Nicht-Angemeldete weiter. Datenbankabfragen soll sie vermeiden, weil sie bei jeder Route läuft, auch bei vorab geladenen. Die Dokumentation sagt deutlich, dass Proxy nicht die einzige Verteidigungslinie sein soll; die meisten Prüfungen gehören so nah wie möglich an die Datenquelle.

Für die Durchsicht bedeutet das: proxy.ts und jede route.ts bekommen besondere Aufmerksamkeit, weil sie viel können. Die Next.js-Dokumentation empfiehlt dafür klassische Methoden bis hin zu regelmäßigen Penetrationstests. Ebenso wichtig ist der Gegenfall: Ein Abmelden über einen Link mit ?logout=1 ist eine Änderung beim Rendern, die die Dokumentation ausdrücklich ablehnt. Änderungen laufen über Actions mit POST.

#Doppeltes Absenden und Anfragen in Serie

Next.js schickt Actions aus einem Browser nacheinander, nicht parallel. Die Dokumentation betont aber, dass das eine Eigenschaft des Clients ist: Der Server stellt nichts in eine Warteschlange, jede Anfrage läuft, sobald sie ankommt. Ein Skript, das dieselbe Action hundertmal aufruft, wird also hundertmal ausgeführt.

Für teure Vorgänge wie den Versand einer Bestätigungsmail empfiehlt die Anleitung eine Begrenzung der Anfragen. Ein Detail aus dem Backend-for-Frontend-Leitfaden wird oft übersehen: Ein Zähler im Arbeitsspeicher zählt je Server-Instanz und beginnt nach einem Neustart bei null. Läuft die Anwendung auf mehreren Instanzen, gehören die Zähler in einen gemeinsamen Speicher oder in die Begrenzung des Hosters. Bei Anmeldeformularen für Veranstaltungen prüfe ich außerdem, dass ein zweites Absenden keine doppelte Anmeldung erzeugt.

#Sicherheitsupdates: was React2Shell gezeigt hat

Am 3. Dezember 2025 veröffentlichte das Next.js-Team die Warnung zu CVE-2025-66478, der unter dem Namen React2Shell bekannt wurde. Die Lücke im Protokoll der React Server Components war mit CVSS 10,0 bewertet und konnte in ungepatchten Anwendungen mit App Router Code auf dem Server ausführen lassen. Eine Umgehung gab es nicht, nur das Update. Drei Tage später kam die Empfehlung, alle Geheimnisse einer Anwendung zu tauschen, wenn sie am 4. Dezember noch ungepatcht online war.

Daraus folgen zwei Zeilen, die in jedem Betriebskonzept stehen sollten: wer Updates einspielt und auf Staging prüft, und wie sich alle Schlüssel einer Anwendung ohne Ausfall tauschen lassen. Das geplante Sicherheitsrelease vom 3. November 2026 brachte die Versionen 16.4.5 und 15.5.29 (Next.js-Blog); laut npm-Registry ist 16.4.6 vom 9. November 2026 die aktuelle Version.

#Was das deutsche Recht und die LDI NRW dazu sagen

§ 19 Abs. 4 TDDDG verpflichtet Anbieter digitaler Dienste, soweit technisch möglich und wirtschaftlich zumutbar, durch technische und organisatorische Vorkehrungen sicherzustellen, dass kein unerlaubter Zugriff auf ihre technischen Einrichtungen möglich ist und diese auch gegen äußere Angriffe gesichert sind. Die Vorkehrungen müssen den Stand der Technik berücksichtigen. Eine Server Action ohne Berechtigungsprüfung ist genau so ein unerlaubter Zugang. Rechtsberatung leiste ich nicht; die Einordnung für Ihr Haus trifft Ihre Datenschutzberatung.

Für Unternehmen in Nordrhein-Westfalen ist die Landesbeauftragte für Datenschutz und Informationsfreiheit zuständig. Die LDI NRW verweist auf das Standard-Datenschutzmodell der Datenschutzkonferenz, das die Anforderungen der DSGVO in Gewährleistungsziele übersetzt. Ich nutze diese Ziele als Raster für die Durchsicht:

GewährleistungszielWas es in einer Server Action bedeutet
VertraulichkeitSitzung und Berechtigung in jeder Action, Geheimnisse nur aus Umgebungsvariablen
IntegritätEingaben auf dem Server prüfen, Besitz aus Sitzung und Datenbank ableiten
VerfügbarkeitBody-Grenze bewusst setzen, Anfragen begrenzen, Zähler gemeinsam speichern
DatenminimierungRückgabewerte und Props auf das beschränken, was die Oberfläche anzeigt

Der Maßnahmenkatalog zum Modell enthält unter anderem die Bausteine 43 „Protokollieren“ und 51 „Zugriffe auf Daten, Systeme und Prozesse regeln“. Abgelehnte Aufrufe einer Action schreibe ich deshalb ins Protokoll, ohne die Formulardaten selbst mitzuschreiben.

#Prüfliste für jede Server Action

  • Die Action prüft die Sitzung selbst, unabhängig von Seite, Layout und proxy.ts.
  • Die Action prüft, ob die Person genau diesen Datensatz ändern darf, und liest den Besitz aus der Datenbank.
  • Jede Eingabe aus FormData, Adresse, Header und Suchparametern wird auf dem Server geprüft.
  • Die Rückgabe enthält nur Status und Felder, die die Oberfläche anzeigt.
  • Kein Geheimnis steht im Quelltext oder in einer Closure.
  • Module mit Datenbankzugriff sind mit server-only markiert.
  • Teure Actions sind begrenzt, und die Zähler liegen bei mehreren Instanzen in einem gemeinsamen Speicher.
  • allowedOrigins enthält nur öffentliche Hosts, die Sie selbst betreiben.
  • Bei mehreren eigenen Servern teilen alle Instanzen denselben NEXT_SERVER_ACTIONS_ENCRYPTION_KEY.
  • Die Next.js-Version ist auf dem Stand des letzten Sicherheitsreleases.

#So läuft ein Auftrag aus Bonn bei mir

  1. Sie schreiben mir, worum es geht: neue Anwendung, Durchsicht einer bestehenden oder ein einzelnes Formular.
  2. Ich stelle Rückfragen schriftlich und schicke einen Umfang mit Annahmen und Ausschlüssen.
  3. Bei einer Durchsicht gehe ich alle Dateien mit use server, die Routen mit Parametern, proxy.ts und route.ts durch und liefere einen Bericht mit Fundstellen.
  4. Korrekturen und neue Funktionen entstehen auf einer Testumgebung, die Sie prüfen und abnehmen.
  5. Danach betreuen Sie die Anwendung selbst, oder ich spiele die Sicherheitsreleases nach Absprache ein.

Für den Start brauche ich Lesezugriff auf das Repository oder eine Kopie, Zugang zur Testumgebung und die Liste Ihrer Formulare. Wenn ein neues Frontend entsteht, liefern Sie das Layout, zum Beispiel als Tabelle, in der Sie markieren, welche Elemente auf welcher Ansicht stehen.

#Verwandte Seiten für Bonn

#Häufige Fragen zu Server Actions

#Reicht es, die Seite mit dem Formular hinter einen Login zu legen?

Nein. Die Next.js-Dokumentation sagt ausdrücklich, dass eine Prüfung auf Seitenebene nicht für die Server Actions auf dieser Seite gilt. Die Seite entscheidet nur, welche Oberfläche jemand sieht. Die Action selbst ist per POST erreichbar und muss Sitzung und Berechtigung selbst prüfen.

#Schützt eine Zod-Prüfung meine Server Action?

Nur zum Teil. Zod oder Valibot prüfen, ob die Eingabe die erwartete Form hat. Ob die übergebene ID zu einem Datensatz gehört, den die anfragende Person ändern darf, prüft das Schema nicht. Dafür lese ich den Datensatz mit der ID und der Nutzer-ID aus der Sitzung neu aus der Datenbank.

#Brauche ich die Einstellung allowedOrigins?

Nur, wenn der Host in der Adresszeile des Browsers nicht bei Next.js ankommt, etwa weil ein Proxy seinen eigenen Host weiterreicht statt X-Forwarded-Host zu setzen. Dann gehört der öffentliche Host in die Liste, nicht der interne. Ein Platzhalter steht dabei für genau einen Teil des Hostnamens, zwei Sterne für einen oder mehrere.

#Sind Werte in einer Closure sicher, weil Next.js sie verschlüsselt?

Next.js verschlüsselt Variablen, die eine Action aus der umgebenden Komponente mitnimmt, und erzeugt dafür bei jedem Build einen neuen Schlüssel. Die Dokumentation rät trotzdem davon ab, sich allein darauf zu verlassen. Geheimnisse und personenbezogene Daten gehören gar nicht in eine Closure, die zum Browser und zurück reist.

#Prüfen Sie auch eine bestehende Next.js-Anwendung?

Ja. Ich gehe alle Dateien mit use server, die Routen mit Parametern in eckigen Klammern sowie proxy.ts und route.ts durch, so wie es die Prüfliste der Next.js-Dokumentation vorschlägt. Sie bekommen einen schriftlichen Bericht mit Fundstellen und Vorschlägen; die Korrekturen setze ich auf Wunsch auf Ihrer Testumgebung um.

#Was soll in Ihrer ersten Nachricht an mich stehen?

Die Adresse der Anwendung, die Next.js-Version aus der package.json, den Hoster oder die Server-Umgebung, die Art der Anmeldung und eine Liste der Formulare mit personenbezogenen Daten. Lesezugriff auf das Repository beschleunigt die Einschätzung, ist für den ersten Schritt aber keine Voraussetzung.

#Ihr Next.js-Vorhaben in Bonn

Ob ein Mitgliederbereich für einen Verband, ein Anmeldeportal für Veranstaltungen oder eine bestehende Anwendung, bei der Sie wissen wollen, wie offen ihre Formulare sind: Schreiben Sie mir über das Kontaktformular. Nennen Sie die Adresse der Anwendung, Ihr Ziel, den gewünschten Termin und den Stack, mit dem Sie heute arbeiten. Ich antworte an Werktagen schriftlich, und die erste Einschätzung zu Ihrem Vorhaben ist kostenlos.

Brauchen Sie die Leistung: Next.js-Entwickler in Bonn?

Beschreiben Sie Ihr Projekt per E-Mail. Antwort innerhalb von 48 Stunden.

Projekt beschreiben

Weitere WordPress-Dienste und Wissensbasis entdecken