Kein Cookie Banner mehr nötig: Ghost CMS komplett ohne Third-Party-Requests einsetzen (docker compose)

Schütze die Privatsphäre deiner Leser und beschleunige deine Ghost-Instanz: Mit nur einer Compose File alle Third-Party-Requests eliminieren und Inhalte mit Redis cachen.

Kein Cookie Banner mehr nötig: Ghost CMS komplett ohne Third-Party-Requests einsetzen (docker compose)
Fast so schnell wie meine Ghost-Instanz mit Redis: Ein Rennauto. Foto von Stephen Kidd auf Unsplash

Kaum eine Website kommt ohne Third-Party-Requests aus. Dabei handelt es sich um alle Anfragen, die eine Website bei einem Besuch an dritte Server schickt. Im Falle von Ghost sind das:

  • JavaScript-Bibliotheken von cdn.jsdelivr.net (der Code für das Ghost Portal, die Suche und, wenn aktiviert, die Kommentarsektion)
  • Embeds. Bei einem YouTube-Video werden beispielsweise Video-Metadaten, ein Thumbnail und die im Player verwendeten Schriftarten geladen.

Third-Party-Requests sind nicht per se schlecht: CDNs liefern vielgenutzte Code-Bibliotheken zuverlässig und schnell aus und dank YouTube spart man sich als kleiner Website-Betreiber einiges an Speicherplatz und Bandbreite, wenn man ab und an mal ein Video zeigen möchte. Problematisch werden Third-Party-Requests jedoch, wenn sie personenbezogene Daten weitergeben – wie die IP-Adresse oder Browser-Fingerprinting-Informationen (User-Agent, Referrer). Betreiber_innen müssen dann eine DSGVO-Rechtsgrundlage sicherstellen, Einwilligungen einholen (in der Regel mithilfe aufdringlicher Cookie-Banner) und Datenübermittlungen in Drittländer verhindern. Macht man das nicht, wird es schnell teuer.

Einbindung von Google Fonts ist rechtswidrig
Wer Schriftarten von Google-Servern ohne Zustimmung einbindet, verstößt gegen die DSGVO. Die Gerichtsentscheidung betrifft aber auch andere CDNs.

Personenbezogene Daten umfassen erst einmal jede Information, mit der Nutzer identifiziert werden können. IP-Adressen, Standortdaten, Browser-Bezeichnungen und Tracking-Cookies ermöglichen Profilbildung und Tracking über mehrere Websites hinweg. Das ist nicht nur ein Datenschutzrisiko, sondern kann auch Performance und Verfügbarkeit beeinträchtigen: Denn für jede Domain muss erst einmal die zugehörige IP-Adresse nachgeschlagen werden. Hat ein externer Anbieter gerade Serverprobleme, so betrifft das alle, die sich auf ihn verlassen. Das passiert häufiger, als man denkt.

Open-Source-CDN JSDelivr: Ausfall trifft zahlreiche Sites weltweit
Ein Wechsel der Zertifizierungsstelle hat das öffentliche Content Delivery Network JSDelivr für einige Stunden lahmgelegt.

Wer seine Ghost-Installation ein bisschen resilienter machen möchte, hat im Grunde zwei Möglichkeiten: Entweder man hostet die Code-Bibliotheken für das Ghost Portal, die Suche und die Kommentarsektion selbst und kümmert sich regelmäßig um Updates. Oder man bezieht weiterhin aktuellsten Versionen der benötigten Dependencies von JSDelivr und speichert sie mithilfe einer effektiven Cache-Policy auf dem eigenen Server zwischen. Wir setzen für diese Anleitung auf Letzteres. Das Prinzip ist simpel:

  • Unter https://meine-website.de betreiben wir ganz normal unsere Ghost-Instanz.
  • Requests, die mit https://meine-website.de/proxy/ beginnen, leiten wir auf einen kleinen, leichten nginx-Server um.
    • Dieser schreibt die angeforderte URL auf die Domain unseres CDN um (https://meine-website.de/proxy/npm/@tryghost/sodo-search@~1.8/umd/sodo-search.min.js => https://cdn.jsdelivr.net/ghost/sodo-search@~1.8/umd/sodo-search.min.js),
    • löscht die Besucher-IP und andere personenbezogene Daten aus dem Header,
    • lädt die angeforderte Ressource selbst schnell herunter,
    • gibt sie der Besucher_in zurück, als hätte sie schon die ganze Zeit auf unserem Server gelegen,
    • und speichert sie für einen definierten Zeitraum zwischen.

Wenn wir schonmal dabei sind, beschleunigen wir auch die Anfragen an unsere Seite. Dazu nutzen wir den eingebauten Redis Cache Adapter.

Diese Anleitung führt Schritt für Schritt durch das Setup der Docker Compose File, unseres nginx‑Proxies für JSDelivr und YouTube-Embeds, Redis und das notwendige Routing. Das YouTube-Proxy ist auf mein Theme Spectre zugeschnitten, das – wenngleich etwas umständlich – datenschutzfreundliche YouTube-Embeds ermöglicht. Der Rest dieses Setups ist aber unabhängig vom Theme.

Voraussetzungen

  • Eine Domain zeigt auf den Server
  • Docker und Docker Compose sind installiert
  • Das externe Docker‑Netzwerk mit dem Namen proxy muss existieren, weil Ghost und der Nginx‑Proxy darüber von Traefik erreichbar gemacht werden, während sie intern zusätzlich das Default‑Netz gemeinsam nutzen.​
  • Traefik v3 dient als bereits laufender Edge‑Proxy für TLS und Routing.​ Wer Unterstützung bei der Installation braucht, findet auf GoNeuland.de eine sehr gute deutschsprachige Schritt‑für‑Schritt‑Anleitung.
Traefik V3 Installation, Konfiguration und CrowdSec-Security
Umfassendste deutschsprachige Anleitung zur Installation und Konfiguration von Traefik V3 und CrowdSec mit Sicherheitsintegration für Docker.

Übersicht

Bevor wir in die Konfiguration einsteigen, schauen wir uns an, wie die einzelnen Komponenten zusammenarbeiten. Unser Setup besteht aus drei Docker-Containern, die über das default Netzwerke miteinander kommunizieren.

Ghost ist das Content Management System. Es bedient alle regulären Anfragen wie Blog-Posts, Seiten, das Admin-Panel oder die ActivityPub-API.

Redis ist ein In-Memory-Datenbank-System, das als Cache-Backend fungiert. Ghost speichert dort beispielsweise vorgerenderte Seiten, Statistiken und Suchergebnisse. Redis ist sehr schnell, weil es alles im Arbeitsspeicher hält, und persistent, weil wir die Daten in ein lokales Verzeichnis schreiben. So bleiben Cache-Einträge auch nach einem Neustart erhalten.

Nginx-Proxy ist ein schlanker Webserver-Container, der ausschließlich für Anfragen unter https://meine-website.de/proxy/zuständig ist. Er leitet diese Anfragen an die jeweiligen Drittdienste weiter (JSDelivr für NPM-Pakete, YouTube für Embeds und Thumbnails), entfernt dabei personenbezogene Daten aus den Request-Headern und cached die Antworten lokal. Für Besucherinnen und Besucher sieht es so aus, als kämen alle Ressourcen direkt vom eigenen Server.

Traefik sitzt als Reverse Proxy vor diesen drei Containern und entscheidet anhand der URL, wohin eine Anfrage geschickt wird. Die Proxying-Software stellt aber auch automatisch SSL-Zertifikate aus oder komprimiert Antworten mit gzip. Die Routing-Logik ist einfach:

  • Anfragen an https://meine-website.de/proxy/* → Nginx-Proxy
  • Alle anderen Anfragen an https://meine-website.de/* → Ghost

Diese Architektur trennt Verantwortlichkeiten sauber: Ghost kümmert sich um Inhalte, Redis um Caching, Nginx um Third-Party-Requests und Traefik um SSL und Routing. Alle Container laufen in Docker, was Deployment, Updates und Wartung vereinfacht. Die gesamte Konfiguration ist in einer docker-compose.yml und wenigen begleitenden Dateien zusammengefasst.

Setup

Repository klonen und ins Projektverzeichnis wechseln

In diesem Beispiel heißt unser Projektordner meine-website.de.

Ordnerstruktur

meine-website.de/
├── docker-compose.yml
├── .env
├── config.production.json
├── nginx-proxy.conf
├── content/ # Ghost-Daten, Themes
├── redis/ # Redis-Persistenz
└── proxy-cache/ # Nginx-Cache

.env kopieren und anpassen

Die Datei example.env liefert sinnvolle Defaultwerte für Domain, SMTP‑Mail, Ghost‑HTTP‑Cache und den Redis‑Cache‑Adapter, die in Compose automatisch in den Ghost‑Container injiziert werden. Dabei steuern die HTTP-Caching-Werte Ghosts eigene HTTP‑Cachezeiten für Seiten, Sitemaps und Assets, während die Redis-Cache-Werte die Redis‑Verbindung, TTL und Retry‑Strategien definieren.

Für den Start reicht es, DOMAIN, die Mail‑Werte und optional die TTL‑Werte für Redis und die HTTP‑Caches zu setzen, um Performance‑ und Aktualitätsanforderungen auszubalancieren.

config.production.json und Compose File überprüfen

Der Ghost‑Service ist auf Produktionsbetrieb ausgelegt, lauscht auf Port 2368 und nutzt SQLite über eine Content‑Volume‑Datei, wodurch Backups leicht möglich sind, weil alle Inhalte und die Datenbank unter /var/lib/ghost/content liegen. Zusätzlich bindet Compose eine eigene config.production.json ein, die Ghost sagt, Portal, Suche und Kommentare nicht direkt von CDNs zu laden, sondern über die Proxy‑Pfade des eigenen Hosts zu beziehen.

In docker-compose.yml sollten Container- und Label-Bezeichnungen ggfs. angepasst werden.

nginx-Proxy im Detail

Die Nginx‑Konfiguration definiert zwei Cache‑Zonen: eine für JSDelivr mit 24 Stunden und eine für YouTube mit einer Stunde, passend zum Änderungsverhalten der Ressourcen.

In den /proxy/npm/‑Locations werden die Header Host, X‑Real‑IP und X‑Forwarded‑For geleert, TLS‑SNI aktiviert und mit proxy_cache_use_stale sowie proxy_cache_lock für Belastungsspitzen und Upstream‑Hänger vorgesorgt, was bewährt ist und den Best Practices des Nginx‑Content‑Caching entspricht.

Für YouTube sind getrennte Locations für oEmbed, Thumbnails und Embeds eingerichtet, die ebenfalls Header strippen, Query‑Parameter korrekt durchreichen und per Cache Header den Status sichtbar machen, was Diagnose und Tuning erleichtert.

Traefik‑Routing im Detail

Die Compose‑Labels teilen den Traffic logisch auf: Eine HTTPS‑Router‑Regel schickt Host(${DOMAIN}) mit !PathPrefix(/proxy/) an Ghost, während eine zweite Regel Host(${DOMAIN}) && PathPrefix(/proxy/) den Nginx‑Proxy anspricht, jeweils mit TLS und passenden EntryPoints. Zusätzlich werden ActivityPub‑Routen explizit per separatem Service an https://ap.ghost.org weitergeleitet, womit Ghosts offizieller ActivityPub‑Endpunkt genutzt wird, ohne die Kerninstanz zu verkomplizieren.

Starten, prüfen, beobachten

Zum Starten im Projektordner die folgenden Befehle ausführen:

Die Logs können wie folgt geprüft werden:

Das Starten der drei Services erledigt Compose in einem Rutsch, inklusive Healthchecks, die etwa im Proxy testweise eine JSDelivr‑Ressource über den eigenen Pfad abrufen, um die Kette zu verifizieren.

In den Logs des Proxy helfen die zusätzlichen Header X‑Cache‑Status und X‑Proxy‑Cache zu erkennen, ob eine Ressource gecacht wurde, während Ghosts Healthcheck die Admin‑API unter der Annahme eines HTTPS‑Forwardings prüft, um falsch konfigurierte Proxies früh zu entdecken.

Anpassungen für eigene Bedürfnisse

Cache‑Dauern lassen sich in nginx-proxy.conf je Location feinjustieren, etwa längere Zeiten für Thumbnails und kürzere für oEmbed, was in produktiven Umgebungen je nach Änderungsfrequenz sinnvoll ist.​

Ghost‑HTTP‑Cachezeiten und Redis‑TTL können per .env variiert werden, um zwischen aggressiverer Performance und höherer Aktualität abzuwägen, beispielsweise bei häufig aktualisierten Startseiten.​

Wer das Spectre‑Theme verwendet, muss das YouTube-Proxying durch folgende Code-Injektion im Site Header aktivieren:

Soweit so gut. Zur Repository geht's hier entlang:

GitHub - hutt/spectre-docker-compose: Docker Compose template for a Ghost installation without eliminated third party requests.
Docker Compose template for a Ghost installation without eliminated third party requests. - hutt/spectre-docker-compose