Performances und sicheres WordPress mit HTP/2 und Nginx
Eine kurze Präambel: diese Artikel stellt keine „HTTP/2-Abhandlung“ dar. Das Ziel soll sein, HTTP/2 in seiner grundlegenden Funktionalität – im Vergleich zu HTTP/1.1 – zu beschreiben, und Zusammenhänge zwischen schnelleren Ladezeiten für WordPress Applikationen und der Einrichtung von HTTP/2 auf Nginx-Basis zu verdeutlichen. Ein zusätzlicher Benefit der hier vorgestellten HTTP/2 Nginx-Konfiguration wird eine „A+“ Bewertung bei SSL-Labs sein, da HTTP/2 mit PFS, HSTS und OCSP Stapling konfiguriert wurde.
Die hier vorgestellten Techniken und Konfigurationen habe ich hier auf WP-Loft.de und meinem privaten Fotoblog (blog.lichttraeumer.de) praktisch umgesetzt.
1. Was macht HTTP/2 nun anders als HTTP/1.1?
HTTP/1.1 Geschichtsstunde, Speedy und was daraus wurde
Der (noch) aktuelle De-facto-Standard um Webseiteninhalte in Browsern anzuzeigen ist das HTTP/1.1 Protokoll. Diesen Standard gibt es nun schon seit 1991, und man liegt mit der Aussage: „HTTP/1.1 ist veraltet“ sicherlich nicht falsch.
Seit den frühen 1990-er Jahren – bis heute – ist der Ressourcen-Hunger und die Komplexität von Webseiten, auch durch die sehr hohe Verbreitung von Contend Management Systemen wie WordPress, rasant angestiegen. Immer mehr CSS-, Javascript- und Font-Ressourcen (Requests) müssen vom Webserver geladen und vom Browser interpretiert und gerendert werden.
Im Grunde erlaubt das HTTP/1.1 Protokoll nur einen offenen Request per TCP-Verbindung. Auch aktuelle Browser unterstützen nur vergleichsweise wenige – zwischen vier und acht, gleichzeitige (concurrent) Verbindungen pro aufgerufener Ursprungsadresse (Origin). Auch mit Keep-Alive oder Pipelining wird beim HTTP/1.1 Protokoll eine Menge Datenoverhead bei der Browser – Webserver Kommunikation produziert.
Auch aktuelle Best-Practice Methoden wie: Spriting von Bildern, Inline CSS– und JavaScript Dateien, als auch die Benutzung von unterschiedlichen Subdomains für unterschiedliche Daten, stellen eher Hacks und Wordarounds als Lösungen dar.
Mitte der 1990-er Jahre wurde, aus den Google-Forschungslaboren heraus, ein Protokoll namens „SPDY“ (Speedy) entwickelt. Zuerst wurde SPDY nur von Google’s Chrome Browser unterstützt, heute sieht das schon anders aus.
Schon damals unterstützte SPDY Dinge wie: Stream-Multiplexing, Aufgabenpriorisierung und HTTP Header-Komprimierung. Ein Webserver mit SSL/TLS Unterstützung war und ist für die Benutzung des SPDY-Protokolles Pflicht.
Aus SPDY wurde im Laufe der Zeit SPDY/2 und damit war die Basis für den im Mai 2015 verabschiedeten HTTP/2 RFC geschaffen.
2. Welche neuen Key-Features bietet HTTP/2?
- HTTP/2 ist binär: Ein binäres Protokoll ist effizienter zu parsen, kompakter „auf der Leitung“ und sehr weniger Fehleranfällig (wie ein textbasiertes Protokoll wie HTTP/1.1)
- HTTP/2 ist multiplexed: Dem Browser (Client) wird erlaubt, in nur einer offen Verbindung pro Ursprungsadresse, den kompletten Inhalt einer URL zu laden. Das sogenannte „Head-of-Line-Blocking“ von HTTP/1.1 gehört damit der Vergangenheit an. Beim Multiplexing werden Request und Response in Streams zugewiesen und auf einzelne Frames verteilt. Mehrere Frames, damnit auch viele einzelne Streams, können it HTTP/2 parallel versendet und empfangen werden.
- HTTP/2 komprimiert Header: nehmen wir an, eine durchschnittliche Webseite besteht aus ca. Achtzig einzelnen Dateien (Bilder, JavaScript und CSS-Dateien, Fonts, Links zu externen Ressourcen etc.). Jede dieser Dateien besitzt einen Header mit (im Schnitt) 1KB Datenvolumen. Auch wenn man in diesem Beispiel dabei in Summe von „nur“ 80kB (eine durchaus realistische Zahl) ausgehen, macht es in der Masse der Daten die übertragen werden schon einen Unterschied, ob diese 80kB Komprimiert sind oder nicht.
HTTP/2 nutzt zur Komprimierung nicht (wie SPDY/2) einen einfachen GZIP Koprimierungs-Kontext (Stichwort: CRIME), sondern ein neues, sicheres Kompressionsverfahren mit dem Namen: „HPACK„ - HTTP/2 unterstützt Server Push: Serve rPush erlaubt dem Webserver, Antwortpakete zu senden, von denen er denkt, der Client (Browser) könnte diese in seinem Cache benötigen. So muss der Browser nicht explizit nach neuen, erforderlichen Elementen beim Webserver nachfragen. Aktuell ist dieses Feature noch nicht in den Nginx implementiert. Nach meinen Recherchen gibt es, stand der Artikelveröffentlichung, auch keine „nicht offiziellen“ Quellen dafür.
- HTTP/2 unterstützt Flow Control: Damit lassen sich die Übertragung der zu sendenden Bytes bestimmen. Der Client kann dem Server „Empfehlungen“ senden, mit welcher Priorität welche Streams bearbeitet werden sollen. Der Server muss sich jedoch nicht zwingend daran halten.
3. Welche Browser unterstützen aktuell den neuen HTTP/2 Standard?
Und: was passiert, wenn mein Browser nicht HTTP/2 kompatibel ist?
Die gute Nachricht ist: alle Browser auf Desktop-Systemen, die den aktuellsten Betriebssystem-Stand haben, unterstützen HTTP/2. Naja … fast. Es ist wie mit diesem gallischen Dorf: Microsoft’s InternetExplorer tanzt mal wider aus der Reihe und unterstützt HTTP/2 ab Version 11 immerhin teilweise.
Die noch bessere Nachricht: sollte ein Browser kein HTTP/2 sprechen, so fällt die Protokollsversion zwischen Client und Server automatisch auf HTTP/1.1 zurück.
Erste Conclusio: Es gibt aus meiner Sicht für Hoster und Root-Server-Betreiber eigentlich keinen Grund nicht auf HTTP/2 zu wechseln!
Eine Liste, welcher Browser HTTP/2 Supporten, oder auch nicht, findet sich hier bei CaniUse.
4. Wir konfiguriert man einen Nginx „sicher“ mit HTTP/2?
Und: Wie bekommt man nebenbei damit ein A+ Rating bei SSL-Labs ?
Präambel:
Das HTTP/2 Protokoll sieht im RFC-Standard eine SSL/TLS Verschlüsselung nicht zwingend vor. Aktuell „bestehen“ jedoch alle Browser zwingend auf eine TLS 1.2 verschlüsselte Verbindung zum Server, um HTTP/2 zzu „sprechen“. Das ist in meinen Augen kein Bug, sondern ein Feature!
Seit der Release-Version 1.9.5 wird der Nginx, und das nicht nur in der Pro-Version, schon mit HTTP/2 Support ausgeliefert. Sofern man die Nginx Paketquellen innerhalb des verwendeten Linux-Derivates benutzt, sollte ein Paketupdate genügen um HTTP/2 Support an Bord zu haben. Ob der installierte Nginx HTTP/2 unterstützt oder nicht, erfährt man mit einem beherzten „nginx -V“ auf der Konsole. Die Ausgabe sollte dann ein „–with-http_v2_module“ beinhalten.
Hinweis: Der Nginx- kennt, im Gegensatz zum Apache-Server, keinen „SSLCACertificateFile“ Parameter. Wird also ein CA-Zertifikat benötigt, so muss dieses händisch an das eigentliche SSL/TLS Zertifikat „gebündelt“ werden. Das ist jedoch auch kein Hexenwerk:
cat eigene-ssl-domain.crt erforderliches-CA-zertifikat.ca > eigene-ssl-domain_bundle.crt
Die Aktivierung des HTTP/2 Protokoll ist ebenso einfach wie simpel. In jedem Nginx-Vhost der HTTP/2 sprechen soll, ist lediglich „http2“ einzufügen:
server {
listen 443 ssl http2;
server_name eigene-ssl-domain.de;
...
ssl_certificate /etc/nginx/ssl/eigene-ssl-domain_bundle.crt;
ssl_certificate_key /etc/nginx/ssl/eigene-ssl-domain.key;
...
}
Um nicht in jedem VHost identische Konfigurationszeilen in Bezug auf SSL/TLS Protokolle und Ciphers pflegen zu müssen, habe ich diese in der nginx.conf stehen:#
## # SSL Settings ## ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:20m; ssl_session_timeout 180m;
Wie üblich bei Nginx-Konfigurationsänderungen:
nginx -t && service nginx restart
Dieses Vorgehen setzt natürlich eine bereits funktionierende SSL/TLS Konfiguration voraus!
Bei eigentlich allen großen Webhostern, die Root- oder VPS-Server im Portfolio haben kann man, auf einem Hoster-Spezifischen „Backend-Panel“, SSL Zertifikate bestellen. Oftmals findet sich dort auch eine detaillierte Kurzdokumentation zum Thema SSL/TLS Zertifikate.
Daher gehe ich an dieser Stelle nicht weiter darauf ein, sondern gehe davon aus, dass ein valides SSL/TLS Zertifikat, der zugehörige Key und ggfs. ein passendes CA-Zertifikat auf dem Server in einem definierten Ordner vorliegen.
5. Wie erreiche ich ein A+ Rating bei SSL-Labs?
Bisher so weit so gut. Damit haben wir SSL/TLS und auch HTTP/2 auf unserem Nginx installiert und konfiguriert. Um bei SSL-Labs einen A+ Status zu bekommen, ist noch ein wenig Handarbeit notwendig. Hierzu müssen im Nginx noch folgende Parameter konfiguriert werden:
- Perfect Forward Secrecy (PFS)
- HTTP Strict Transport Security (HSTS)
- Online Certificate Status Protocol stapling (OCSP-stapling)
Auch hier möchte ich nicht auf die technischen Standards eingehen. Daher habe ich als erste Informationsquelle den jeweils zughörigen Wikipedia-Link hinterlegt.
1. Perfect Forward Secrecy (PFS)
Hierfür wird lediglich ein „Diffie-Hellmann Key“ benötigt, der wie folgt erzeugt (die Erzeugung des Keys kann ein wenig dauern):
openssl dhparam -out /etc/nginx/dhparam.pem 4096
Der Key wird einfach in die Nginx-Konfiguration eingebaut:
... ssl_dhparam dh_4096.pem; ...
2. HTTP Strict Transport Security (HSTS)
Hierfür wird der SSL/TLS Konfiguration zusätzliche Header konfiguriert:
add_header Strict-Transport-Security "max-age=31536000" always;
3. Online Certificate Status Protocol stapling (OCSP-stapling)
(ab Nginx Version 1.3.7)
Hinweis: Betreibt man mehrere Webseiten / VHosts auf dem Nginx, so muss OCSP-Stapling in jedem VHost, der SSL/TLS sprechen soll, eingerichtet werden. Sonst funktioniert das OCSP-Stapling nicht.
Ist auf dem Nginx die Zertifikatskette korrekt, also mit Root-Zertifikat, dem zugehörigen Key und dem passenden CA-Zertifikat konfiguriert, genügen die folgenden Zeilen um OCSP-Stapling einzuschalten:
ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s;
Nach einem neu laden der Nginx-Konfiguration lässt die die Funktionalität des OCSP-Stapling lokal testen:
openssl s_client -CApath /etc/ssl/certs/ -connect eigene-ssl-domain.de:443 -tls1 -tlsextdebug -status
Die letzte Zeile der Konsole-Ausgabe sollte so aussehen:
Verify return code: 0 (ok)
Alternativ kann man auch die SSL-Labs Seite bemühen und im Browser nach „OCSP“ suchen.
4. Das Sahnehäubchen: der X-Frame-Options Header
Mit dem X-Frame-Options Header kann Clickjacking verhindert werden:
add_header X-Frame-Options "SAMEORIGIN";
Anstatt „SAMEORIGIN“ kann auch“DENY“ oder „ALLOW-FROM“ gesetzt werden.
Viel Spass mit HTTP/2 und dem A+ Rating bei SSL-Labs 🙂
By the way: die hier vorgestellten Techniken und Konfigurationen sind natürlich nicht WordPress spezifisch und funktionieren auch für andere CMS’se und / oder Webapplikationen auf einem Nginx-Server.