Eine alte Domain weiterleiten, ohne Links zu verlieren
Mit go-redirect, einem kleinen Docker-Container, leitest du eine aufgegebene Domain samt Pfaden auf eine neue um. Drei Einstellungen gehen dabei leicht daneben.

Wenn eine Domain aufgegeben wird, hängen an ihr noch Lesezeichen, Links aus alten Beiträgen und Einträge bei Google. Lässt du sie einfach auslaufen, enden all diese Wege in einem Fehler. Eine Weiterleitung, die jeden Pfad an die passende Stelle der neuen Domain schickt, hält sie am Leben.
Mein Werkzeug dafür ist go-redirect, ein Go-Programm in einem Docker-Container, das auf jede Anfrage mit einem Redirect antwortet. Konfiguriert wird es nur über ein paar Umgebungsvariablen. Laut README ist das Image rund 5 MB groß, ein statisches Binary auf scratch.
Der einfache Fall
Eine Domain soll komplett auf eine andere zeigen, mit allen Pfaden:
services:
redirect:
image: ghcr.io/danielgtmn/go-redirect:latest
ports:
- "80:8080"
environment:
- REDIRECT_TARGET=https://neue-domain.de
- REDIRECT_CODE=301
- PRESERVE_PATH=true
restart: unless-stopped
Aus alte-domain.de/blog/artikel?seite=2 wird dann https://neue-domain.de/blog/artikel?seite=2. Das funktioniert nur, wenn die Pfade auf der neuen Seite dieselben sind. Hast du beim Umzug Slugs geändert, gehören diese Fälle auf der neuen Seite in eine eigene Weiterleitung.
REDIRECT_CODE kennt zwei Werte. 301 sagt Browsern und Suchmaschinen, dass der Umzug dauerhaft ist, und ist für eine aufgegebene Domain das Richtige. 302 nimmst du, solange du dir noch nicht sicher bist. Jeder andere Wert fällt ohne Meldung auf 301 zurück.
Drei Einstellungen, die leicht danebengehen
Die erste ist PRESERVE_PATH. Im README steht als Standard true, der Code übernimmt Pfad und Query aber nur, wenn die Variable genau true lautet. Fehlt sie, landet jede Anfrage auf der Startseite des Ziels. Setz sie also immer ausdrücklich. Für Besucher sieht das aus wie ein kaputter Link, und Google bekommt für jede alte Unterseite dieselbe Antwort.
Die zweite ist ein Schrägstrich am Ende von REDIRECT_TARGET. Der Pfad wird direkt an das Ziel gehängt. Aus https://neue-domain.de/ plus /kontakt wird https://neue-domain.de//kontakt. Viele Server kommen damit klar, schön ist es trotzdem nicht. Gib das Ziel deshalb ohne Schrägstrich am Ende an.
Die dritte ist die Scanner-Sperre. Mit BLOCK_SCANNERS=true beantwortet der Container typische Pfade von Schwachstellen-Scannern mit 404, zum Beispiel .env, wp-admin oder phpmyadmin. Er vergleicht dabei Teilstrings im Pfad, ohne auf Groß- und Kleinschreibung zu achten. Auf der Liste stehen auch allgemeine Wörter wie admin, backup, debug, trace oder graphql und Endungen wie .php. Ein alter Artikel unter /blog/backup-strategie bekäme also ein 404 statt einer Weiterleitung. Geh deine alten URLs einmal gegen die Liste durch. Passt etwas nicht, legst du eine eigene JSON-Datei an und hängst sie über BLOCKED_PATHS_FILE oder als Volume auf /scanner_paths.json ein.
Subdomains mitnehmen
Hat die alte Domain Subdomains, reicht ein einzelnes Ziel nicht. Dafür gibt es REDIRECT_DOMAIN_MAP im Format quelle:ziel:
docker run -d \
-p 80:8080 \
-e REDIRECT_DOMAIN_MAP=alte-domain.de:neue-domain.de \
ghcr.io/danielgtmn/go-redirect:latest
Dann wird aus blog.alte-domain.de/post die Adresse https://blog.neue-domain.de/post. Pfad und Query bleiben in diesem Modus immer erhalten, unabhängig von PRESERVE_PATH. Das Schema kommt aus REDIRECT_SCHEME und ist standardmäßig https. Hosts, die nicht zur Quelle passen, bekommen ein 404, außer du setzt zusätzlich REDIRECT_TARGET als Auffangziel.
TLS kommt vom Proxy
go-redirect spricht nur HTTP, standardmäßig auf Port 8080. Wer https://alte-domain.de aufruft, braucht aber ein gültiges Zertifikat für die alte Domain, sonst sieht er eine Zertifikatswarnung, bevor die Weiterleitung überhaupt greift. Das übernimmt der Reverse Proxy vor dem Container, in Coolify etwa Caddy oder Traefik.
Dort gilt dieselbe Reihenfolge wie bei jedem neuen Hostnamen: erst DNS, dann die Domain im Proxy eintragen. Warum, habe ich im Beitrag über Hindsight auf Coolify beschrieben. Kurz gesagt fragt der Proxy sonst Zertifikate an, die er nicht validieren kann, und läuft in das Rate-Limit von Let's Encrypt.
Logs ohne IP-Adressen
Jede Weiterleitung landet als Zeile im Log, mit Methode, Pfad und Ziel. Die IP-Adresse ersetzt der Container dabei vollständig durch x.x.x.x beziehungsweise x:x:x:x:x:x:x:x. Du siehst also, welche alten Pfade noch aufgerufen werden, ohne personenbezogene Daten zu sammeln. Mit LOG_LEVEL=none schaltest du die Zeilen ganz ab.
Für einen Healthcheck gibt es /health, der mit OK antwortet. Dieser eine Pfad wird deshalb nie weitergeleitet.
Prüfen, ob es läuft
Ein Aufruf mit curl -I zeigt Statuscode und Ziel, ohne dem Redirect zu folgen:
curl -I "https://alte-domain.de/blog/artikel?seite=2"
In der Antwort sollten 301 und eine Location-Zeile mit der vollständigen neuen Adresse stehen, Pfad und Query inklusive. Steht dort nur die Startseite, fehlt PRESERVE_PATH=true.
Für Google lohnt sich danach noch die Adressänderung in der Search Console. Sie setzt voraus, dass beide Domains dort bestätigt sind und die 301-Weiterleitungen schon greifen. Abschalten kannst du den Container, wenn im Log über längere Zeit keine alten Pfade mehr auftauchen.