Wagtail Multi-Site: Mehrere Seiten aus einer Codebase
Ein Django-Projekt, ein Server, beliebig viele Auftritte – wie das Sites-Framework das möglich macht
Mehrere Webseiten bedeuten normalerweise mehrere Deployments, mehrere Datenbanken, mehrfache Wartung. Wagtail kann das anders: Ein Projekt, eine Datenbank, ein Deployment – und trotzdem beliebig viele Domains mit jeweils eigenem Look und eigenen Inhaltstypen. Ich habe devmaker.net von Anfang an auf diesem Fundament gebaut. Online ist aktuell nur dieser Blog, aber das Setup trägt mehr: Ich habe es mit einem News-Ticker und einem Science-Portal als zweite und dritte Site getestet.
Der Trick ist kein Hack, sondern eine Kernfunktion von Wagtail.
Das Wagtail Sites-Framework in einem Satz
Wagtail kann mehrere Sites in einer Installation verwalten. Jede Site ist im Kern nur ein Mapping: Hostname (+ Port) → Root-Page. Kommt ein Request rein, schaut Wagtail in der Site-Tabelle nach, welcher Hostname passt, und rendert den darunterliegenden Seitenbaum. Genau eine Site ist die is_default_site – sie fängt alles ab, was sonst nicht matcht.
Schematisch (mit Beispiel-Hosts für die nicht-öffentlichen Seiten):
Hostname Root-Page Status
-----------------------------------------------------------
devmaker.net HomePage live
news.example.com HomePage Beispiel / nicht online
papers.example.com PaperHomePage Beispiel / nicht online
Wichtig: Die Root-Pages sind komplett getrennte Bäume. Slugs dürfen sich über Sites hinweg wiederholen – ein /home-automation/ auf der einen Site kollidiert nicht mit einem gleichnamigen Pfad auf einer anderen, weil jeder Baum unter seiner eigenen Root hängt.
Eigene Page-Typen pro Domain
Das ist der eigentliche Hebel. Multi-Site heißt nicht „dreimal dasselbe mit anderem Logo“. Jede Site darf ihre eigenen Page-Modelle nutzen. Ein Blog und ein News-Ticker können sich HomePage/TopicPage/ArticlePage teilen, ein Science-Portal bekommt dagegen eigene Typen für Paper:
# pages/models.py
from wagtail.models import Page
class HomePage(Page):
"""Startseite für Blog- und News-Sites."""
subpage_types = ["pages.TopicPage"]
class ArticlePage(Page):
"""Klassischer Blog-/News-Artikel (StreamField-Body)."""
parent_page_types = ["pages.TopicPage"]
class PaperHomePage(Page):
"""Eigene Startseite für ein Science-Portal."""
subpage_types = ["papers.PaperTopicPage"]
class PaperArticlePage(Page):
"""Wissenschaftliches Paper in Plain Language."""
parent_page_types = ["papers.PaperTopicPage"]
# ... DOI, Autoren, Original-Abstract, Rewrite ...
Über subpage_types und parent_page_types erzwinge ich, dass unter einer PaperHomePage nur Paper-Typen landen können – und unter einer HomePage nur Blog-Typen. So bleiben die Welten sauber getrennt, obwohl sie im selben Admin liegen.
Templates: geteilt, wo es geht – eigen, wo nötig
Wagtail leitet das Template automatisch aus dem Page-Typ ab: HomePage → home_page.html, PaperHomePage → paper_home_page.html. Gemeinsame Bausteine (Header, Footer, das Terminal-Editorial-Grundgerüst) liegen in einem base.html, das alle erben. Site-spezifische Unterschiede leben nur im jeweiligen Template.
Wenn ich pro Site doch dasselbe Modell mit anderem Look brauche, hilft ein Blick auf den Hostname im Context:
class ArticlePage(Page):
def get_template(self, request, *args, **kwargs):
site = request.site if hasattr(request, "site") else None
host = getattr(site, "hostname", "")
if host.startswith("news."):
return "pages/article_page_news.html" # kompakter News-Look
return "pages/article_page.html" # ausführlicher Blog-Look
Ein Reverse Proxy, mehrere Domains
Netzwerkseitig ist es unspektakulär – und genau das ist der Punkt. Alle Domains zeigen per DNS auf dieselbe öffentliche IP. Ein Reverse Proxy im Homelab terminiert TLS für jeden Hostname und reicht sie an denselben Gunicorn-Upstream weiter. Wagtail entscheidet anhand des Host-Headers, welche Site gerendert wird:
# Ein Upstream für alle Domains
upstream wagtail { server 127.0.0.1:8000; }
server {
listen 443 ssl;
server_name devmaker.net news.example.com papers.example.com;
# TLS pro Host (z. B. via Let's Encrypt / Nginx Proxy Manager)
ssl_certificate /etc/letsencrypt/live/multi/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/multi/privkey.pem;
location / {
proxy_pass http://wagtail;
proxy_set_header Host $host; # <- entscheidend!
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Die eine Zeile, auf die es ankommt, ist proxy_set_header Host $host;. Ohne den korrekten Host-Header sieht Django immer nur die Proxy-Adresse – und Wagtail fällt auf die Default-Site zurück. Damit Django die Hosts überhaupt akzeptiert, müssen sie in den Settings stehen:
# settings/production.py
ALLOWED_HOSTS = [
"devmaker.net",
"news.example.com",
"papers.example.com",
]
# Hinter dem Reverse Proxy: HTTPS korrekt erkennen
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
USE_X_FORWARDED_HOST = True
Locales teilen sich alle Sites
devmaker.net ist zweisprachig (Deutsch als Default, Englisch dazu) – und dieselbe Mechanik gilt site-übergreifend. Die Locales sind global, nicht pro Site. Mit wagtail-localize bekommt jede Page einen Übersetzungsbaum, und der Sprachpräfix läuft über i18n_patterns. So teilt sich der komplette Übersetzungs-Workflow über alle Marken hinweg – ein Tooling, beliebig viele Auftritte.
Genau eine Site darf is_default_site=True sein. Vergisst du das beim Anlegen einer neuen Site, beantwortet Wagtail unbekannte Hosts entweder gar nicht oder mit der falschen Seite. Symptom: Eine neue Domain zeigt plötzlich den Blog statt der erwarteten Seite.
Was geteilt wird – und was nicht
- Geteilt: Datenbank, Media-Storage, Bilder, Snippets, User & Permissions, das Admin-Backend, Locales, Deployment.
- Getrennt: Seitenbäume, Page-Typen, Templates, URLs/Slugs, Sitemaps pro Host.
Das gemeinsame Media-Pool ist Komfort und Risiko zugleich: Ein Bild, das ich für die eine Site hochlade, ist im Admin auch bei der anderen wählbar. Mit Collections lässt sich das pro Marke ordnen, wenn nötig.
Lessons Learned
- Multi-Site spart real Wartung. Ein
git pull, eine Migration, ein Neustart – alle Seiten sind aktuell. Das ist der größte Gewinn gegenüber getrennten Deployments. - Von Anfang an Multi-Site-fähig bauen lohnt sich. Auch wenn nur eine Seite online geht: Das Fundament steht, und eine zweite Site ist später eine Frage von Stunden, nicht von Tagen.
- Eigene Page-Typen sind der Unterschied zwischen „echt verschiedene Seiten“ und „Themes“. Plane sie früh, nicht als Nachgedanke.
- Der Host-Header ist die häufigste Fehlerquelle. Die meisten „falsche Seite wird angezeigt“-Bugs hängen am Reverse Proxy, nicht an Wagtail.
Fazit
Wagtail Multi-Site ist eine der unterschätztesten Funktionen des CMS. Wer mehrere Projekte plant – oder sich die Tür dafür offenhalten will – spart sich mit einem sauberen Sites-Setup massiv Infrastruktur, ohne die Seiten inhaltlich zu verbiegen. Bei mir trägt das Fundament aktuell devmaker.net und ist für weitere Auftritte vorbereitet: pragmatisch, nicht überengineered.