WordPress zu Astro migrieren: Schritt für Schritt

12 Min. Lesezeit Read in English

Kurz gesagt

Eine WordPress-Website lässt sich in sieben Schritten nach Astro umziehen: URLs inventarisieren, Inhalte nach Markdown exportieren, URL-Struktur festlegen, Weiterleitungen bauen, SEO-Daten 1:1 übernehmen, Formulare und Suche ersetzen, kontrolliert starten. Über die Rankings entscheidet am Ende die Weiterleitungstabelle: Jede alte URL muss nach dem Umzug mit 200, 301 oder bewusst mit 410 antworten. Die schwerste Handarbeit steckt meist nicht in den Texten, sondern in Page-Builder-Markup und Shortcodes.

Eine WordPress-Website nach Astro umzuziehen ist kein Hexenwerk, aber auch kein Export-Knopf. Was Besucher und Google am Ende sehen, entscheidet sich an wenigen Stellen: ob jede alte Adresse noch irgendwohin führt, ob Titel und Beschreibungen mitkommen und ob nichts Wichtiges stillschweigend verschwindet. Dieser Artikel führt dich in der Reihenfolge durch, in der wir die Arbeit angehen würden.

Ob der Wechsel für dich überhaupt sinnvoll ist, klärt Astro vs. WordPress. Hier gehen wir davon aus, dass die Entscheidung gefallen ist und es ums Wie geht.

1. Inventur: Was ist da, und was davon zählt?

Bevor du eine Zeile Astro schreibst, brauchst du eine vollständige Liste dessen, was heute unter deiner Domain erreichbar ist.

URLs sammeln. Die Liste entsteht aus mehreren Quellen, weil keine allein vollständig ist:

  • die XML-Sitemap (/wp-sitemap.xml bei WordPress-Bordmitteln, sonst die deines SEO-Plugins),
  • ein Crawl mit einem Tool deiner Wahl, das auch interne Links verfolgt (so findest du Seiten, die in keiner Sitemap stehen),
  • der Export der Seiten mit Impressionen und Klicks aus der Search Console.

Das Ergebnis ist eine Tabelle: eine Zeile pro URL, daneben Seitentyp (Seite, Beitrag, Kategorie, Anhang, Archiv), Klicks, Impressionen und die Zahl der eingehenden Links. Letztere bekommst du aus der Search Console im Bericht „Links“ oder aus einem Backlink-Tool.

Gewichten. Nicht jede URL ist gleich wichtig. Seiten mit Traffic und Backlinks müssen nahtlos weiterleben. Veraltete Beiträge ohne Traffic und ohne Links darfst du zusammenlegen oder entfernen, aber bewusst und mit einem Ziel in der Tabelle, nicht durch Vergessen.

Plugins nach Funktion auflisten. Der Name des Plugins ist egal, entscheidend ist, was es auf der Website tut. Für jedes Plugin gibt es drei mögliche Antworten: Die Funktion entfällt, Astro kann sie nativ, oder sie braucht einen Ersatz. Typische Kandidaten:

Funktion unter WordPress Gegenstück bei Astro
SEO-Plugin (Titel, Beschreibung, Schema, Sitemap) Felder im Frontmatter, Layout-Komponente, @astrojs/sitemap
Caching- und Optimierungs-Plugins entfallen, Astro liefert fertiges HTML
Formular-Plugin eigener Endpunkt oder Dienst, siehe Schritt 6
Shortcodes (Buttons, Akkordeons, Galerien) Astro-Komponenten oder Markdown
Mehrsprachigkeit (WPML, Polylang) Ordnerstruktur und Routing, siehe Schritt 3
Suche, Kommentare, Mitgliederbereich Ersatz nötig oder bewusst streichen

Ein Shop mit WooCommerce ist ein eigener Fall: Warenkorb und Checkout gewinnen durch eine statische Website nichts. Dafür ist der Headless-Weg gedacht, den Headless WordPress mit Astro beschreibt.

2. Inhalte exportieren: Markdown mit Frontmatter

Das Ziel: Jede Seite und jeder Beitrag wird eine Markdown-Datei mit Frontmatter für Titel, Beschreibung, Datum und alles, was die Seite sonst braucht. Zwei Wege führen dorthin.

Der WXR-Export (Werkzeuge, Daten exportieren) liefert eine XML-Datei mit allem. Praktisch für einen einmaligen Rundumschlag, aber du musst sie selbst parsen.

Die REST-API liefert dasselbe als JSON und ist leichter zu skripten. Jede WordPress-Installation bringt sie mit; Beiträge liegen unter /wp-json/wp/v2/posts, Seiten unter /wp-json/wp/v2/pages. Pro Anfrage sind höchstens 100 Einträge möglich, also blätterst du durch:

// export.mjs – Beiträge als Markdown-Dateien ablegen
import { writeFile, mkdir } from 'node:fs/promises';
import TurndownService from 'turndown';

const base = 'https://example.de/wp-json/wp/v2';
const turndown = new TurndownService({ headingStyle: 'atx' });
await mkdir('src/content/blog', { recursive: true });

let page = 1;
let totalPages = 1;
do {
  const res = await fetch(`${base}/posts?per_page=100&page=${page}`);
  totalPages = Number(res.headers.get('X-WP-TotalPages'));
  for (const post of await res.json()) {
    const body = turndown.turndown(post.content.rendered);
    const frontmatter = [
      '---',
      `title: ${JSON.stringify(post.title.rendered)}`,
      `description: ${JSON.stringify(post.excerpt.rendered.replace(/<[^>]+>/g, '').trim())}`,
      `pubDate: ${post.date.slice(0, 10)}`,
      `wpId: ${post.id}`,
      '---',
    ].join('\n');
    await writeFile(`src/content/blog/${post.slug}.md`, `${frontmatter}\n\n${body}\n`);
  }
  page++;
} while (page <= totalPages);

Das Skript ist ein Ausgangspunkt, kein fertiges Werkzeug. Titel enthalten HTML-Entitäten, Auszüge taugen selten als Meta-Beschreibung, und wenn du ein SEO-Plugin einsetzt, liegen die echten Meta-Titel und -Beschreibungen in dessen Feldern (Yoast liefert sie zum Beispiel in der REST-Antwort unter yoast_head_json). Dazu mehr in Schritt 5.

Bilder brauchen einen eigenen Durchlauf. Das Skript sammelt alle Adressen aus /wp-content/uploads/, lädt die Dateien herunter und schreibt die Pfade im Markdown um. Bilder, die im Fließtext stehen, legst du in src/assets/, damit Astro sie optimiert. Dateien, die unter der alten Adresse erreichbar bleiben sollen, kommen in public/, mehr dazu in Schritt 4. Vergiss die Alternativtexte nicht: Sie stehen im Medienarchiv, nicht im Beitragstext.

Shortcodes und Page-Builder-Markup sind der schwierige Teil. [button], [gallery] und vor allem die verschachtelten Container von Elementor, Divi oder WPBakery lassen sich nicht sinnvoll automatisch umwandeln. Was bleibt, ist ein Mix aus Regeln und Handarbeit:

  1. Zähle, welche Shortcodes und Bausteine überhaupt vorkommen, und sortiere nach Häufigkeit.
  2. Baue für die häufigen eine Astro-Komponente und übersetze sie per Skript, zum Beispiel [button url="…"]Text[/button] in <Button href="…">Text</Button>. Dafür brauchst du MDX.
  3. Seltene Sonderfälle, besonders gestaltete Landingpages, baust du als eigene .astro-Seite neu, statt sie aus dem Builder-Markup zu rekonstruieren.

Content Collections mit Schema. Damit unvollständige Inhalte den Build brechen, statt unbemerkt online zu gehen, beschreibst du das Frontmatter in src/content.config.ts mit einem zod-Schema (Quelle: Astro-Dokumentation, „Content Collections“):

import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({
    title: z.string().min(10).max(70),
    description: z.string().min(120).max(160),
    pubDate: z.coerce.date(),
    wpId: z.number().optional(),
  }),
});

export const collections = { blog };

Verletzt eine Datei das Schema, meldet Astro das mit einer Fehlermeldung. Gerade bei einem Export mit hunderten Dateien ist das Gold wert: Du siehst sofort, welche Beschreibungen fehlen oder zu lang sind.

3. URL-Struktur: Adressen behalten, wo es geht

Die beste Weiterleitung ist die, die du nicht brauchst. Bleibt eine URL gleich, ändert sich für Google nichts. Prüfe deshalb zuerst, was du 1:1 übernehmen kannst:

  • Schrägstrich am Ende. WordPress-Permalinks enden meist auf /. Setze in astro.config.mjs trailingSlash: 'always', damit Astro nur diese Form kennt. Laut Dokumentation ist der Standard 'ignore'.
  • Ausgabeformat. build.format steht standardmäßig auf 'directory': Aus /ueber-uns/ wird ueber-uns/index.html, passend zu WordPress-Adressen. Nur wenn du Adressen mit .html behalten musst, nimmst du 'file'.
  • Kategorien und Paginierung. Hatten deine Beiträge URLs wie /category/seo/ oder /blog/page/2/, legst du entsprechende Routen mit getStaticPaths() und paginate() an. Ob die Struktur bleibt oder sich vereinfacht, ist eine Designentscheidung; Hauptsache, jede alte Adresse landet in der Tabelle aus Schritt 4.
  • Slugs. Der Export liefert den WordPress-Slug mit, also die letzte URL-Stufe. Nutze ihn unverändert als Dateinamen.
// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://example.de',
  trailingSlash: 'always',
  build: { format: 'directory' },
  integrations: [sitemap()],
});

Ist die Website mehrsprachig, plane die Struktur jetzt, nicht später. Wie das bei einer statischen Seite aussieht, steht in Astro mehrsprachig: zwei Domains, zwei Sprachen, ein Build.

4. Weiterleitungen: Hier entscheiden sich die Rankings

Die Redirect-Tabelle ist das wichtigste Dokument der Migration. Jede URL aus deinem Inventar bekommt ein Ziel: gleiche Adresse (kein Eintrag nötig), neue Adresse (301) oder bewusst weg (410). Nichts darf in einem 404 enden, das früher Besucher hatte.

301 vom Server, nicht aus Astro. Astro kennt eine Option redirects in der Konfiguration:

export default defineConfig({
  redirects: {
    '/category/seo/': '/blog/seo/',
    '/alter-beitrag/': '/blog/neuer-beitrag/',
  },
});

Das ist praktisch, hat bei statischem Hosting aber eine Einschränkung, die man kennen muss. Laut Astro-Dokumentation erzeugt astro build bei statischen Websites HTML-Dateien mit einem Meta-Refresh-Tag; der Statuscode wird dann vom Server nicht verwendet. Der Server antwortet also mit 200, und die Weiterleitung geschieht erst im Browser. Das ist kein echtes 301. Wer Rankings sichern will, setzt die Regeln deshalb in die Serverkonfiguration. Auf Apache ist das die .htaccess:

# Einzelne Seiten
Redirect 301 /alter-beitrag/ /blog/neuer-beitrag/

# Ganzes Präfix
RedirectMatch 301 ^/category/(.*)$ /blog/$1

Bei nginx sieht dasselbe so aus:

location = /alter-beitrag/ { return 301 /blog/neuer-beitrag/; }
rewrite ^/category/(.*)$ /blog/$1 permanent;

Bei mehr als einer Handvoll Einträge erzeugt unser Redirect-Generator aus einer Liste alter und neuer URLs die fertigen Regeln für Apache oder nginx.

WordPress-Altlasten. Ein paar Adressen kennt dein Inventar nicht, weil WordPress sie automatisch erzeugt hat. Sie tauchen trotzdem in Links und im Index auf:

Alte Adresse Empfehlung
/feed/, /comments/feed/ 301 auf einen neuen Feed oder die Beitragsübersicht, sonst 410
/?p=123 (Kurzlinks) 301 auf die neue Adresse des Beitrags
/wp-admin/, /wp-login.php 410 oder einfach 404; sie gehören zu keinem Inhalt
/wp-content/uploads/… Datei weiter ausliefern oder 301 auf die neue Datei, wenn extern verlinkt (Bilder, PDFs)
/author/…, /2023/05/, /tag/… 301 auf die passende Übersicht oder 410, wenn die Archive entfallen
/category/… 301 auf die neue Kategorie oder die Übersicht

Die Kurzlinks mit ?p= brauchen eine Bedingung auf den Query-String, weil Redirect ihn nicht auswertet:

RewriteEngine On
RewriteCond %{QUERY_STRING} ^p=123$
RewriteRule ^$ /blog/neuer-beitrag/? [R=301,L]

Das abschließende ? verhindert, dass der Query-String am Ziel kleben bleibt. Für gelöschte Bereiche nimmst du Redirect gone /wp-admin/ (Apache) oder return 410; (nginx).

Bei Uploads gilt: Hängen noch fremde Seiten an deinen Bildern, kopierst du die Originale nach public/wp-content/uploads/ und lässt sie dort liegen. Das ist die einfachste Weiterleitung. Astro vergibt für optimierte Bilder neue Dateinamen, deshalb funktioniert die Umleitung nur für Dateien, die du unverändert bereitstellst.

410 oder 301? Ein 410 („Gone“) sagt klar, dass etwas absichtlich entfernt wurde. Eine 301 gehört nur dorthin, wo das Ziel inhaltlich passt. Alle toten Seiten pauschal auf die Startseite zu leiten ist kein Ersatz: Google kann das als Soft-404 werten (Hinweis aus der Google-Search-Central-Dokumentation).

5. SEO-Daten übernehmen

Die Faustregel: Alles, was Google heute sieht, soll morgen genauso aussehen.

  • Titel und Beschreibungen 1:1. Die Meta-Daten stehen meist im Feld deines SEO-Plugins, nicht im Beitragstitel. Exportiere sie mit und schreibe sie ins Frontmatter. Ranking-Titel formulierst du nicht um, auch nicht aus Geschmacksgründen. Verbessern kannst du später, einzeln und messbar.
  • Canonical. Jede Seite bekommt ein selbstreferenzierendes Canonical auf die endgültige Adresse, mit Schrägstrich und der richtigen Domain.
  • hreflang bei mehreren Sprachen, wechselseitig und mit absoluten URLs.
  • Strukturierte Daten. Organisation, Breadcrumbs, Article und FAQ, die dein Plugin erzeugt hat, baust du als JSON-LD in die Layout-Komponente nach. Danach prüfst du sie mit dem Test für Rich-Ergebnisse. Was Astro in Sachen SEO von Haus aus mitbringt und was nicht, steht in Astro SEO.
  • Sitemap. @astrojs/sitemap erzeugt sie beim Build, braucht dafür aber die Option site. Seiten, die nicht in den Index sollen, nimmst du heraus.
  • robots.txt. Als Datei in public/, mit Verweis auf die neue Sitemap. Prüfe, dass nicht noch das Disallow: / der Staging-Umgebung drinsteht.
  • 404-Seite. src/pages/404.astro wird beim Build zur 404.html; auf Apache trägst du sie mit ErrorDocument 404 /404.html ein. Eine gute 404 enthält Navigation, keine Sackgasse.
  • Interne Links. Der Export enthält absolute Links auf die alte Adresse und auf /wp-content/. Ersetze sie durch relative Pfade, sonst läuft jeder interne Klick über eine Weiterleitung.

6. Formulare, Suche und Kommentare

Eine statische Website hat keine Datenbank und kein PHP, also brauchen dynamische Funktionen ein Gegenstück.

Formulare. Zwei saubere Wege: ein kleiner Endpunkt oder ein Dienst. Läuft die Website ohnehin auf einem Hosting mit PHP, reicht ein kurzes Skript, das Eingaben prüft und per SMTP verschickt (wir nutzen auf unserer eigenen Website PHPMailer). Spamschutz, Pflichtfelder und eine Fehlermeldung bei Misserfolg gehören dazu. Teste den Versand regelmäßig, denn ein kaputtes Formular fällt niemandem auf. Alternativ nimmst du einen Formular-Dienst; dann prüfe, wo die Daten liegen und ob ein Auftragsverarbeitungsvertrag möglich ist.

Suche. Für Websites bis zu einer gewissen Größe genügt eine statische Suche, die nach dem Build einen Index erzeugt, zum Beispiel Pagefind. Sie braucht keinen Server. Bei sehr großen Beständen oder vielen Filtern lohnt ein Suchdienst.

Kommentare. Hier hilft eine ehrliche Bilanz: Wie viele Kommentare gab es in den letzten zwölf Monaten? Meist lautet die Antwort „kaum welche“, und dann ist Streichen die beste Lösung. Bestehende Kommentare kannst du als Text archivieren, wenn sie Inhalt tragen. Wer Diskussion braucht, bindet ein externes System ein, mit Blick auf den Datenschutz.

Mitgliederbereiche, Buchungen, Shops. Alles mit Login und Zustand ist kein Fall für eine rein statische Seite. Dort bleibt WordPress oder ein anderes System im Hintergrund, und Astro übernimmt die Auslieferung, wie in Headless WordPress mit Astro beschrieben.

7. Launch und danach

Vor dem Umzug. Baue die neue Website auf einer Staging-Adresse mit noindex und teste dort gegen deine URL-Liste. Dafür reicht ein kleines Skript, das jede alte URL anfragt und Statuscode und Ziel ausgibt:

while read -r url; do
  printf '%s -> ' "$url"
  curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' "$url"
done < alte-urls.txt

Jede Zeile muss 200, 301 (mit passendem Ziel) oder bewusst 410 zeigen. Alles andere ist ein Fehler, den du vor dem Launch beheben willst. Prüfe außerdem, dass noindex und die Sperre in der robots.txt vom Staging nicht mit live gehen.

Launch-Checkliste:

  1. Neue Website veröffentlichen, Weiterleitungen aktiv.
  2. Das Skript von oben gegen die Live-Adresse laufen lassen.
  3. Stichproben: Startseite, die zehn wichtigsten Seiten nach Traffic, Kategorie- und Paginierungsseiten, ein Bild aus /wp-content/uploads/.
  4. Formular absenden und die Mail wirklich ankommen sehen.
  5. Sitemap in der Search Console einreichen.
  6. Wechselt die Domain, nutzt du zusätzlich das Tool zur Adressänderung in der Search Console (Google Search Central, Dokumentation zu Website-Umzügen). Bleibt die Domain gleich, entfällt das.
  7. Tracking prüfen: Wird die neue Website gemessen?

Nach dem Launch. In den ersten Tagen schaust du in der Search Console auf den Bericht zur Seitenindexierung und auf die 404-Meldungen. Jede 404-URL mit früherem Traffic bekommt einen Eintrag in der Weiterleitungstabelle. Den Traffic vergleichst du vier bis acht Wochen lang wöchentlich mit dem Stand vor dem Umzug. Leichte Schwankungen sind normal, weil Google die neue Version erst neu bewertet. Fällt eine Gruppe von Seiten deutlich ab, suchst du dort zuerst: Fehlt eine Weiterleitung, hat sich ein Titel geändert, ist ein Canonical falsch?

Die Weiterleitungen lässt du bestehen, am besten dauerhaft. Google empfiehlt in seiner Dokumentation zu Website-Umzügen, sie mindestens ein Jahr beizubehalten.

Was den Aufwand treibt

Eine pauschale Antwort gibt es nicht, aber es gibt klare Treiber:

  • Seitenzahl: Mehr Seiten bedeuten mehr Prüfung und mehr Weiterleitungen, auch wenn der Export automatisch läuft.
  • Page Builder: Je mehr Layout im Builder-Markup steckt, desto mehr Seiten musst du neu aufbauen statt konvertieren.
  • Plugins mit sichtbarer Funktion: Jedes davon braucht ein Gegenstück oder eine Entscheidung.
  • Mehrsprachigkeit: Jede Sprache verdoppelt Inventar, Weiterleitungen und Prüfungen, dazu kommt hreflang.
  • Zustand des Bestands: Alte Beiträge mit kaputten Links, fehlenden Bildern und Duplikaten kosten Zeit, die man vorher nicht sieht.

Wer vorher zählt, wie viele Seiten, Shortcodes, Plugins und Sprachen im Spiel sind, kann den Aufwand realistisch einschätzen. Die Inventur aus Schritt 1 ist deshalb auch die Grundlage jeder Kalkulation. Wie sich das bei einem kompletten Relaunch zusammensetzt, zeigt Website-Relaunch: Kosten, Ablauf und die teuersten SEO-Fallen.

Wenn du es lieber abgeben möchtest

Du kannst alle diese Schritte selbst gehen. Fehlen dir Zeit oder Lust, übernehmen wir das von der Inventur bis zur Kontrolle nach dem Launch. Wie wir dabei vorgehen, beschreibt die Seite WordPress-zu-Astro-Migration.

Häufige Fragen

Kann ich WordPress selbst nach Astro migrieren?

Ja, wenn die Website überschaubar ist und du mit Node.js, Git und einer Konfigurationsdatei umgehen kannst. Der Export der Inhalte ist mit der WordPress-REST-API gut machbar. Aufwendig wird es bei Page Buildern, vielen Plugins, mehreren Sprachen und großen URL-Beständen, weil dort Handarbeit und Prüfung zunehmen.

Verliere ich beim Umzug von WordPress zu Astro meine Rankings?

Nicht zwangsläufig. Wenn URLs erhalten bleiben oder sauber per 301 weitergeleitet werden und Titel, Beschreibungen, Canonicals und strukturierte Daten übernommen sind, behält Google die Zuordnung bei. Kurze Schwankungen nach dem Launch sind normal, eine Garantie gibt es nicht.

Reichen die Weiterleitungen in der Astro-Konfiguration?

Auf statischem Hosting nicht. Dort erzeugt Astro für jede Weiterleitung eine kleine HTML-Seite mit Meta-Refresh, der Server antwortet aber mit 200 statt 301. Für echte 301 gehören die Regeln in die Serverkonfiguration, etwa in die .htaccess oder in nginx.

Wie behandle ich /feed/, /wp-admin/ und Autorenarchive?

Alles, was ein Gegenstück hat, bekommt eine 301 auf die passende neue Seite, zum Beispiel /feed/ auf die Übersicht oder einen neuen Feed. Was es künftig nicht mehr gibt, etwa /wp-admin/ oder Datums- und Autorenarchive, beantwortest du mit 410 oder leitest es auf die nächstpassende Übersicht um.

Wie lange sollte ich nach dem Launch beobachten?

Mindestens vier bis acht Wochen. In der Search Console schaust du auf Indexierung und 404-Fehler, den Traffic vergleichst du wöchentlich mit dem Stand vor dem Umzug. Die Weiterleitungen selbst lässt du deutlich länger bestehen.

Daniel Nilges
Daniel Nilges

Gründer & Full-Stack-Entwickler

20+ Jahre Erfahrung in der Webentwicklung. Spezialisiert auf Laravel, WordPress und individuelle Software für den Mittelstand.