Migration zu Next.js / Astro in Nürnberg

Was beim Umzug eines WordPress-Mitgliederbereichs mit Konten, Rollen, Passwörtern und geschützten Downloads passiert und wie Astro oder Next.js den Zugang danach prüfen.

Migration zu Next.js / Astro

Website- & Anwendungsmigration in Nürnberg

Ich migriere Websites von WordPress, Joomla, Drupal, Angular, Vue und anderen Technologien zu Astro und Next.js. Jede Migration plane ich so, dass SEO, Inhalte und Funktionen erhalten bleiben, und schalte in einem vereinbarten Zeitfenster um. Ich arbeite allein und habe langjährige Erfahrung mit älteren und modernen Stacks.

  • 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

Ihre WordPress-Website hat einen Bereich, den nicht jeder sehen soll: Datenblätter für Händler, Unterlagen für Kunden, ein internes Verzeichnis für Mitglieder. Bei einer Migration zu Astro oder Next.js zieht dieser Teil nicht einfach mit. Konten, Rollen, Passwörter und Dateien folgen eigenen Regeln, und eine Lücke an dieser Stelle fällt erst auf, wenn jemand ein PDF ohne Anmeldung öffnet.

Mein Name ist Mariusz Szatkowski. Ich entwickle seit 2006 mit WordPress, inzwischen auch mit Astro und Next.js, als einzelner Entwickler aus Gdynia, und ich arbeite schriftlich. Diese Seite beschreibt, wie ich einen geschützten Bereich aus WordPress übernehme: was in der Datenbank steht, was mit den Passwörtern geschieht und wie das neue System den Zugang prüft.

#Wo geschützte Bereiche in Nürnberg typisch sind

Die Wirtschaftsförderung der Stadt zählt auf ihrer Seite zum Wirtschaftsstandort rund 25.000 Unternehmen in Nürnberg. Als Branchen nennt sie unter anderem Maschinenbau, Mess-, Steuer- und Regeltechnik, Automatisierung, Softwareentwicklung, Ingenieur- und Wirtschaftsberatung sowie Versicherungs- und Finanzdienstleistungen. Auf ihrer Seite zu Clustern und Netzwerken führt sie Netzwerke auf, mehrere davon als eingetragene Vereine, etwa die ENERGIEregion Nürnberg e.V. und das European Center for Power Electronics e.V.

Aus dieser Liste ergeben sich die Fälle, für die ich einen geschützten Bereich baue: technische Unterlagen aus Maschinenbau und Messtechnik, die nur Vertriebspartner sehen sollen, Dokumente einer Beratung oder eines Versicherungsmaklers für die eigene Kundschaft, interne Seiten eines Netzwerks für seine Mitglieder. Ob Ihr Fall einer davon ist, sehen Sie an der Bestandsaufnahme im nächsten Abschnitt.

#Bestandsaufnahme: was hinter dem Login liegt

Bevor ich etwas umbaue, liste ich auf, wie der Schutz heute funktioniert. In WordPress gibt es dafür mehrere Mechanismen, die oft nebeneinander laufen:

  • Benutzerkonten mit Rollen. Jedes Konto hat eine Rolle, die festlegt, was die Person darf.
  • Private Beiträge und Seiten. Laut WordPress-Dokumentation sehen sie nur Benutzer mit der Rolle Administrator oder Editor.
  • Passwortgeschützte Beiträge und Seiten. Ein Kennwort pro Seite, ohne Benutzerkonto.
  • Plugins für Mitgliedschaften oder Downloads. Sie legen eigene Tabellen, Rollen oder Regeln an, die ich einzeln lesen muss.
  • Dateien in der Mediathek. Für jede geschützte Datei prüfe ich, ob sie unter ihrer eigenen Adresse auch ohne Anmeldung abrufbar ist.

Für jeden Mechanismus halte ich schriftlich fest, welche Inhalte betroffen sind und wer heute Zugang hat. Diese Liste ist später die Grundlage für die Abnahme.

#Rollen und Fähigkeiten: was mitwandert

WordPress kennt laut Dokumentation zu Rollen und Fähigkeiten sechs vordefinierte Rollen: Super Admin, Administrator, Editor, Author, Contributor und Subscriber. Jede Rolle bündelt Fähigkeiten wie publish_posts oder edit_users. Der Subscriber hat nur die Fähigkeit read und kann laut Dokumentation lediglich sein Profil verwalten. Mitglieder tragen in WordPress meist diese Rolle oder eine eigene, die ein Plugin oder ein Entwickler mit add_role() angelegt hat. Welche es bei Ihnen ist, zeigt die Datenbank.

Für die Migration ist eine Unterscheidung wichtig. Die Rollen des Redaktionsteams betreffen das CMS, in dem künftig geschrieben wird. Wie sich das nach dem Umzug ändert, beschreibt die Seite zur Redaktion nach der Migration in Hannover. Die Rollen der Mitglieder betreffen dagegen die Website selbst: Sie entscheiden, welche Seiten und Dateien jemand nach der Anmeldung sieht.

Ich übertrage deshalb nicht das Rollensystem von WordPress als Ganzes, sondern nur das, was die neue Website prüfen muss. Aus der Tabelle der Benutzer und den Metadaten entsteht pro Konto ein kleiner Datensatz: Kennung, E-Mail-Adresse, Passwort-Hash und eine Rolle wie mitglied oder haendler. Fähigkeiten, die nur im WordPress-Backend Sinn ergeben, lasse ich weg.

#Passwortgeschützte Seiten sind kein Mitgliederbereich

Viele kleinere Websites schützen Unterlagen nur mit dem Kennwortfeld einer Seite. Die WordPress-Dokumentation beschreibt die Grenzen dieses Verfahrens genau:

  • Das Kennwort ist auf 20 Zeichen begrenzt.
  • WordPress merkt sich das Kennwort in einem Cookie im Browser und verfolgt immer nur ein Kennwort. Wer zwei Seiten mit verschiedenen Kennwörtern öffnet, muss das erste danach erneut eingeben.
  • Inhalt und Auszug werden erst nach Eingabe des Kennworts ausgegeben, Daten aus benutzerdefinierten Feldern (Custom Fields) aber nicht automatisch geschützt. Das Theme muss sie mit post_password_required() selbst zurückhalten.

Bei der Bestandsaufnahme prüfe ich deshalb, ob Ihr Theme benutzerdefinierte Felder auf geschützten Seiten ausgibt. Für das neue System empfehle ich meist, solche Seiten in den Bereich mit Konten zu überführen. Ein gemeinsames Kennwort, das alle Händler kennen, lässt sich nicht für eine einzelne Person sperren.

#Was mit den Passwörtern passiert

WordPress speichert keine Passwörter, sondern Hashes. Wie diese aussehen, hat sich mit Version 6.8 geändert. Laut Änderungsprotokoll von wp_hash_password() werden Passwörter seit 6.8 standardmäßig mit bcrypt gehasht statt mit phpass. Der Quelltext auf derselben Seite zeigt den Ablauf: Das Passwort wird mit HMAC-SHA-384 vorgehasht, das Ergebnis mit bcrypt gehasht und mit dem Präfix $wp gespeichert.

Die Ankündigung des Core-Teams erklärt die Gründe und die Folgen:

  • Das Vorhashen umgeht die Grenze von 72 Byte, die bcrypt bei Passwörtern setzt. Neue Hashes beginnen deshalb mit $wp$2y$.
  • Beim ersten Login nach dem Update oder bei der nächsten Passwortänderung wird ein altes Passwort automatisch mit bcrypt neu gehasht. Wer sich seitdem nicht angemeldet hat, behält den alten Hash.
  • Das alte Präfix $P$ für phpass-Hashes bleibt laut Ankündigung weit verbreitet. Daneben können reine bcrypt-Hashes mit $2y$ aus Plugins, Argon2-Hashes und alte MD5-Hashes mit 32 Hexadezimalzeichen vorkommen.
  • Anwendungspasswörter und Schlüssel zum Zurücksetzen werden mit BLAKE2b gehasht und tragen das Präfix $generic$.
  • Die Benutzerpasswörter stehen in der Spalte wp_users.user_pass.

Die Prüfung in wp_check_password() bestätigt das: Sie erkennt Hashes mit $wp, mit $P$ und reine MD5-Werte und prüft jeden auf seine Weise. In einer älteren Datenbank können also mehrere Formate nebeneinander stehen. Bevor ich über den Weg entscheide, zähle ich deshalb, wie viele Konten welches Präfix haben.

#Zwei Wege: Zurücksetzen oder Hashes übernehmen

Zurücksetzen erzwingenHashes übernehmen
Was die Mitglieder merkeneine E-Mail mit Link, danach ein neues Passwortnichts, die Anmeldung funktioniert wie bisher
Was ich baueImportskript ohne Hashes, Versand der LinksPrüfung pro Hash-Format, neuer Hash nach dem ersten Login
RisikoMitglieder ohne gültige E-Mail-Adresse kommen nicht mehr hineinCode für alte Formate bleibt, bis alle Konten neu gehasht sind
Passt, wennwenige Konten, viele alte Formate, ohnehin ein Neustartviele aktive Mitglieder, überwiegend $wp$2y$

In der Praxis kombiniere ich beides. Hashes mit $wp$2y$ prüfe ich im neuen System direkt. Eine Prüfung für $P$ baue ich nur ein, wenn viele aktive Konten noch dieses Format tragen und sie auf Staging mit Testkonten aus Ihrer Datenbank besteht. Alle übrigen Formate bekommen beim Import kein Passwort, die betroffenen Konten erhalten einen Link zum Zurücksetzen.

Die Prüfung eines $wp$2y$-Hashes in Node.js ist kurz. Sie bildet den Zweig für $wp in wp_check_password() nach und vergleicht dann mit bcrypt:

import { createHmac } from "node:crypto";
import bcrypt from "bcryptjs";

// Nur für WordPress-Hashes mit dem Präfix $wp$2y$ (WordPress 6.8 und neuer)
export function checkWpBcrypt(password, storedHash) {
  if (!storedHash.startsWith("$wp$2y$")) return false;
  const prehash = createHmac("sha384", "wp-sha384")
    .update(password)
    .digest("base64");
  return bcrypt.compare(prehash, storedHash.slice(3));
}

Gibt die Funktion true zurück, speichere ich sofort einen neuen Hash im Verfahren des neuen Systems und lösche den alten. Die Bibliothek Better Auth etwa hasht laut Dokumentation standardmäßig mit scrypt und erlaubt eigene Funktionen zum Hashen und Prüfen. Dort hänge ich die Prüfung für das alte Format ein. Nach einigen Monaten sehen Sie in einer Auswertung, wie viele Konten noch alte Hashes tragen, und entscheiden, ob der Rest einen Link zum Zurücksetzen bekommt.

Eine Sache gilt für beide Wege: Der Export der Benutzertabelle ist eine Datei mit Personendaten. Ich lege sie nicht in Repository oder Ticket ab, übertrage sie verschlüsselt und lösche sie nach dem Import.

#Geschützte Seiten in Astro

Astro rendert laut Dokumentation zum Rendering auf Anfrage standardmäßig alle Seiten beim Build als statisches HTML. Eine Seite, die einem angemeldeten Mitglied seine Unterlagen zeigt, muss aber pro Anfrage entstehen. Dafür braucht das Projekt einen Adapter für die Laufzeitumgebung, Astro pflegt offizielle Adapter für Node.js, Netlify, Vercel und Cloudflare. Die einzelne Seite bekommt export const prerender = false, der Rest der Website bleibt statisch.

Die Middleware-Dokumentation sagt ausdrücklich, dass Middleware für vorgerenderte Seiten beim Build läuft und nur bei Seiten mit Rendering auf Anfrage zum Zeitpunkt der Anfrage. Eine Prüfung der Anmeldung in src/middleware.ts schützt eine vorgerenderte Seite also nicht: Deren HTML liegt nach dem Build fertig auf dem Server. Ich prüfe deshalb zwei Dinge:

  1. Jede Seite im geschützten Bereich hat prerender = false, und ein Test auf Staging ruft sie ohne Anmeldung auf und erwartet eine Weiterleitung zum Login.
  2. Die Middleware legt die angemeldete Person in context.locals ab. Laut Dokumentation existiert dieses Objekt nur für eine einzelne Anfrage, die Seite liest es und entscheidet selbst.

Für den Zustand zwischen den Anfragen gibt es Sessions. Sie liegen auf dem Server, nicht im Cookie, und brauchen einen Speichertreiber. Die Adapter für Node.js, Cloudflare und Netlify richten laut Dokumentation einen Standardtreiber ein, bei anderen Adaptern muss er angegeben werden.

Eine eigene Anmeldelösung hat Astro nicht. Die Anleitung zur Authentifizierung sagt das so und verweist auf Bibliotheken der Community, darunter Better Auth, das Astro direkt unterstützt. Wie ich Inhalte aus WordPress in Astro bringe, steht auf der Seite zum WordPress-Export in Essen.

#Geschützte Seiten in Next.js

Die Next.js-Anleitung zur Authentifizierung empfiehlt für mehr Sicherheit und weniger Aufwand eine Bibliothek, statt Anmeldung und Sitzung selbst zu bauen. Die Berechtigung prüft sie an einer zentralen Stelle, der Datenzugriffsschicht, die nur auf dem Server läuft und nur die Felder zurückgibt, die eine Seite braucht. Der Proxy, die frühere Middleware, ist dort optional und nur für schnelle Prüfungen anhand des Cookies gedacht, weil er bei jeder Route läuft, auch bei vorab geladenen.

Für die Migration zählen zwei Punkte aus der Anleitung. Route Handler gelten als öffentlich erreichbare Endpunkte und müssen selbst prüfen, ob die Person zugreifen darf. Und eine Prüfung im Layout reicht nicht, weil Layouts bei der Navigation nicht neu gerendert werden. Ausführlich beschreibe ich Sitzungen, Cookies und die Datenzugriffsschicht auf der Seite zur Next.js-Anmeldung in Wiesbaden.

#Dateien, die niemand per Adresse erreichen darf

Bei geschützten Downloads entscheidet der Ablageort. Beide Frameworks haben einen Ordner, dessen Inhalt öffentlich ist:

  • Astro kopiert laut Dokumentation zur Projektstruktur den Ordner public/ unverändert in den Build.
  • Next.js liefert Dateien aus public/ laut Dokumentation unter ihrer Adresse ab der Wurzel aus: public/avatars/me.png ist unter /avatars/me.png erreichbar.

Ein PDF für Händler gehört also in keinem der beiden Ordner. Ich lege geschützte Dateien in einen Speicher ohne öffentliche Adresse und liefere sie über einen Endpunkt aus. In Astro ist das ein Server-Endpunkt mit prerender = false, der laut Dokumentation zu Endpunkten eine Antwort mit Binärdaten zurückgeben kann. In Next.js ist es ein Route Handler. Beide prüfen zuerst Sitzung und Rolle und geben erst dann die Datei heraus, sonst Status 403.

In der alten WordPress-Installation prüfe ich vor dem Umzug, ob die bisher geschützten Dateien unter ihrer Adresse im Upload-Ordner ohne Anmeldung abrufbar sind. Ist das der Fall, waren sie es schon vorher, und Suchmaschinen oder alte E-Mails können die Adressen kennen. Diese Adressen leite ich nach der Migration nicht auf die neue Datei um, sondern auf die Anmeldeseite.

#Mitgliederdaten, Verzeichnisse und alte Konten

Für private Unternehmen und Vereine in Bayern ist das Bayerische Landesamt für Datenschutzaufsicht (BayLDA) zuständig. Zu Vereinen schreibt es, dass die Verarbeitung von Mitgliederdaten für die Mitgliederverwaltung auf Art. 6 Abs. 1 Buchst. b DSGVO gestützt werden kann, eine Einwilligung ist dafür nicht nötig. Anders bei Kontaktdaten unter Mitgliedern: Die Mitglieder sind untereinander Dritte. E-Mail-Adressen an andere Mitglieder herauszugeben ist laut BayLDA grundsätzlich nur zulässig, wenn die Betroffenen eingewilligt haben, sofern der Vereinszweck nicht vor allem auf Kontaktpflege gerichtet ist.

Für ein Mitgliederverzeichnis im neuen System folgt daraus eine technische Anforderung. Jedes Feld, das andere Mitglieder sehen, braucht eine eigene Freigabe pro Person, und das Verzeichnis zeigt nur freigegebene Felder. Hat das alte Plugin solche Freigaben gespeichert, übernehme ich sie. Fehlen sie, zeige ich im neuen Verzeichnis zunächst nur, was zur Mitgliedschaft selbst gehört, und Sie holen die Einwilligungen ein. Rechtsberatung gebe ich nicht, die Einordnung trifft Ihre Datenschutzberatung.

Zum Löschen schreibt das BayLDA, dass personenbezogene Daten grundsätzlich unverzüglich zu löschen sind, wenn es keine Rechtsgrundlage für die weitere Speicherung mehr gibt, auch ohne Verlangen der Betroffenen. Eine Migration ist der Moment, in dem Konten ausgeschiedener Händler oder ehemaliger Mitglieder auffallen. Ich liefere vor dem Import eine Liste aller Konten mit Rolle und, soweit gespeichert, dem Datum der letzten Anmeldung. Sie entscheiden, welche Konten nicht mitziehen.

Wöhrder See in Nürnberg

Foto: Buendia22, CC BY-SA 4.0, Wikimedia Commons.

#Prüfliste vor dem Umzug Ihres geschützten Bereichs

Diese Fragen helfen Ihnen auch dann, wenn Sie die Migration mit jemand anderem planen:

  1. Welche Inhalte sind heute geschützt, und mit welchem Mechanismus: Rolle, privater Beitrag, Seitenkennwort oder Plugin?
  2. Wie viele Konten gibt es, mit welcher Rolle, und wie viele haben sich im letzten Jahr angemeldet?
  3. Welche Präfixe haben die Hashes in wp_users.user_pass, und wie viele Konten entfallen auf jedes?
  4. Hat jedes Konto eine gültige E-Mail-Adresse für einen Link zum Zurücksetzen?
  5. Welche Dateien sind geschützt, und sind sie heute ohne Anmeldung unter ihrer Adresse abrufbar?
  6. Sehen Mitglieder Daten anderer Mitglieder, und gibt es dafür gespeicherte Freigaben?
  7. Welche Konten sollen nicht mehr übernommen werden?
  8. Wer verwaltet nach dem Umzug Konten und Rollen, und in welcher Oberfläche?

#So läuft die Migration eines Mitgliederbereichs mit mir

Sie schicken mir die Adresse der Website und, soweit bekannt, Antworten auf die Prüfliste. Mit einem Lesezugang zur Datenbank oder einem Export, der statt der Hashes nur deren Präfix enthält, erstelle ich die Bestandsaufnahme: Mechanismen, Rollen, Zahl der Konten pro Hash-Format, geschützte Dateien. Rückfragen stelle ich schriftlich. Danach erhalten Sie einen schriftlichen Umfang mit Annahmen, darin auch die Entscheidung zwischen Zurücksetzen und Hash-Übernahme.

Gebaut wird auf einer Staging-Umgebung mit Testkonten, nicht mit den echten Mitgliederdaten. Die echten Konten importiere ich erst kurz vor dem Start. Zur Abnahme gehört eine Liste von Adressen, die ohne Anmeldung, als Mitglied und als Händler aufgerufen werden, mit dem erwarteten Ergebnis für jede Rolle. Wie ich die Qualitätsprüfung vor dem Start insgesamt aufbaue, steht auf der Seite zur Prüfung vor dem Livegang in Zürich, die Weiterleitungen alter Adressen auf der Seite zu Weiterleitungen in Bremen. Gestaltung, Mockups und Wireframes für Login und Mitgliederbereich kommen von Ihnen, ich setze sie um. Eine Übersicht über den gesamten Ablauf finden Sie auf der Seite zur Migration zu Next.js und Astro, Projekte nur mit einem der Frameworks auf den Seiten zu Astro in Nürnberg und Next.js in Nürnberg.

#Fragen zu Konten, Passwörtern und geschützten Dateien

Müssen alle Mitglieder nach der Migration ein neues Passwort vergeben? Nicht zwingend. Hashes mit dem Präfix $wp$2y$ lassen sich im neuen System prüfen, ohne dass ich das Passwort kenne. Nach der ersten erfolgreichen Anmeldung speichere ich einen neuen Hash. Für Konten mit Hash-Formaten, die ich nicht sauber prüfen kann, erzwinge ich ein Zurücksetzen per Link.

Sehen Sie bei der Migration die Passwörter meiner Mitglieder? Nein. WordPress speichert nur Hashes in der Spalte user_pass, nicht die Passwörter selbst. Ich übertrage diese Hashes, die Datei mit den Kontodaten behandle ich trotzdem wie Personendaten und lösche sie nach dem Import.

Reicht der Passwortschutz von WordPress-Seiten für einen Kundenbereich? Für einzelne Seiten mit einem gemeinsamen Kennwort ja, für einen Kundenbereich nicht. Das Kennwort ist auf 20 Zeichen begrenzt, gilt für alle gleich, und Inhalte aus benutzerdefinierten Feldern schützt es laut WordPress-Dokumentation nicht.

Kann eine Astro-Website überhaupt einen Login haben? Ja, mit einem Adapter und Rendering auf Anfrage für die geschützten Seiten. Der Rest bleibt statisch. Astro bringt keine offizielle Anmeldelösung mit, die Dokumentation nennt Bibliotheken wie Better Auth.

Wo liegen geschützte PDF-Dateien nach dem Umzug? Nicht im Ordner public, denn dessen Dateien sind über ihre Adresse erreichbar. Ich lege sie in einen Speicher ohne öffentliche Adresse und liefere sie über einen Endpunkt aus, der vorher die Anmeldung und die Rolle prüft.

Was passiert mit Konten, die seit Jahren niemand benutzt? Das entscheiden Sie vor dem Import. Personendaten ohne Rechtsgrundlage für die weitere Speicherung sind nach DSGVO zu löschen. Ich liefere eine Liste der Konten mit dem Datum der letzten Anmeldung, sofern WordPress oder ein Plugin es gespeichert hat.

#Schicken Sie mir Ihren Login-Bestand

Wenn Ihr Unternehmen oder Verein in Nürnberg einen geschützten Bereich auf WordPress betreibt und auf Astro oder Next.js umziehen will, schreiben Sie mir über das Kontaktformular. Hilfreich sind die Adresse der Website, Ihr Ziel, ein gewünschter Termin, die eingesetzten Plugins für Mitgliedschaft oder Downloads und eine grobe Zahl der Konten. Hashes oder Mitgliederdaten schicken Sie bitte nicht mit, die brauche ich erst im Projekt. Sie bekommen von mir eine schriftliche Ersteinschätzung ohne Kosten, mit einer Empfehlung, ob Ihre Mitglieder ein neues Passwort brauchen oder sich wie gewohnt anmelden können.

Brauchen Sie die Leistung: Migration zu Next.js / Astro in Nürnberg?

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

Projekt beschreiben

Weitere WordPress-Dienste und Wissensbasis entdecken