Clientseitiges XSLT ersetzen: XML mit PHP, Python oder Node.js auf dem Server umwandeln
Chrome wendet ab dem 17. November 2026 keine XSL-Stylesheets mehr an. Wenn Ihre Website XML mit einem solchen Stylesheet ausliefert, ist es die dauerhafteste Lösung, die Umwandlung auf Ihrem Server auszuführen und HTML zu senden. Die Beispiele für Python und Node.js in diesem Artikel haben wir selbst getestet; das PHP-Beispiel beruht auf dem PHP-Handbuch.
Was ersetzt clientseitiges XSLT in Chrome?
XSLT serverseitig: Ihr Server führt die XSL-Umwandlung aus und schickt dem Browser fertiges HTML. In PHP übernimmt das die Klasse XSLTProcessor der XSL-Erweiterung, in Python die Klasse XSLT von lxml, in Node.js ein Paket wie saxon-js oder xslt-processor. Alle drei erwarten Ihre XML- und Ihre XSL-Datei und liefern einen HTML-String, den Sie als text/html senden. Nur auf diesem Weg hängt das Ergebnis nicht mehr davon ab, was der Browser kann.
Ab Chrome 158 (17. November 2026) funktioniert XSLT nicht mehr. Websites im Origin Trial und Browser mit Unternehmensrichtlinie behalten es bis Chrome 176 am 17. August 2027. Unsere Übersicht zum Ende von XSLT in Chrome zeigt, welche Seiten betroffen sind, und nennt alle Lösungen für Besucher.
Chromes Ankündigung beschreibt für Website-Betreiber diese Wege:
- Die XSLT-Verarbeitung auf dem Server ausführen und das erzeugte HTML senden.
- Das Skript xslt-polyfill in das XML-Dokument einfügen, damit das Stylesheet im Browser wieder angewendet wird.
- JSON statt XML ausliefern und es mit JavaScript darstellen.
- XSLT mit einer JavaScript-Bibliothek auf der Seite ausführen. Die Ankündigung bezeichnet die Bibliothek von Saxonica als die mit Abstand größte; sie unterstützt XSLT 3.0 vollständig.
Das Polyfill ist die kleinste Änderung. Wie die Bibliothek aus Punkt 4 läuft es im Browser jedes Besuchers. Die Umwandlung auf dem Server macht mehr Arbeit, dafür bekommt der Browser eine gewöhnliche HTML-Seite, ganz ohne Skript und Erweiterung. Der größte Teil dieses Artikels behandelt diesen Weg.
Brauchen Sie mehr Zeit, funktioniert XSLT auf Ihrer Website mit dem Origin Trial von Chrome noch bis Version 176 im August 2027. Dazu melden Sie Ihre Website für das Trial an und senden das Token mit den Antworten Ihrer Seiten mit. XML-Seiten haben keinen HTML-head für ein meta-Tag; nutzen Sie daher den HTTP-Antwortheader Origin-Trial, den Chromes Anleitung zu Origin Trials beschreibt. Wenn Sie den Server gar nicht ändern können, bleibt das Polyfill die Lösung.
Sollte ich meine Sitemap oder meinen RSS-Feed in HTML umwandeln?
Nein. Suchmaschinen und Feedreader rufen sie ab und erwarten unter dieser Adresse XML.
Googles Dokumentation zu Sitemaps nennt die unterstützten Formate: XML-Sitemaps, RSS, mRSS und Atom 1.0 sowie Textdateien. HTML gehört nicht dazu. Wer /sitemap.xml durch HTML ersetzt, macht die Datei für Suchmaschinen unbrauchbar, nur um ein Anzeigeproblem zu lösen, das ausschließlich menschliche Leser betrifft. Unser Artikel darüber, was Google in einer Sitemap liest, zeigt, dass das Layout dabei keine Rolle spielt.
Lassen Sie das XML, wo es ist. Wenn Besucher eine lesbare Fassung brauchen, veröffentlichen Sie das HTML unter einer anderen Adresse und verlinken es. Für Feeds gibt es eine einfachere Lösung: Feeds mit CSS gestalten funktioniert in Chrome weiterhin.
Die Umwandlung auf dem Server eignet sich für XML-Seiten, die allein für menschliche Leser gedacht sind, etwa einen mit XSL gestalteten Produktkatalog.
Warum liefert mein XSLT für eine Sitemap eine leere Tabelle?
Weil die Elemente einer Sitemap in einem Namespace liegen und das Stylesheet sie mit einem Präfix ansprechen muss. Die Beispiele in diesem Artikel erzeugen aus zwei Dateien eine lesbare Kopie einer Sitemap, die unter einer eigenen Adresse wie /sitemap.html erscheint; /sitemap.xml bleibt unverändert. Das XML ist eine kurze Sitemap:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://example.com/</loc><lastmod>2026-10-14</lastmod></url>
<url><loc>https://example.com/about/</loc></url>
</urlset>
Das Stylesheet sitemap.xsl macht daraus eine HTML-Tabelle:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" exclude-result-prefixes="s"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s="http://www.sitemaps.org/schemas/sitemap/0.9">
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<h1>Sitemap</h1>
<table>
<xsl:for-each select="s:urlset/s:url">
<tr>
<td><xsl:value-of select="s:loc"/></td>
<td><xsl:value-of select="s:lastmod"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
XSLT 1.0 übernimmt seine Pfadausdrücke aus der W3C-Empfehlung XML Path Language 1.0. Dort passt ein Name ohne Präfix nur auf Elemente ohne Namespace; den Standard-Namespace des Dokuments berücksichtigt die Empfehlung nicht. Deshalb deklariert das Stylesheet das Präfix s und schreibt s:urlset/s:url. Mit urlset/url bliebe die Tabelle leer.
Das Attribut exclude-result-prefixes="s" hält diese Namespace-Deklaration aus dem HTML heraus. Ohne das Attribut begann die Ausgabe in unserem Test mit lxml so: <html xmlns:s="...">.
Mit libxslt, das die Beispiele für PHP und Python nutzen, hat die Ausgabe keinen DOCTYPE: In unserem Test mit lxml begann sie direkt mit <html><body>. Laut MDN braucht eine Seite einen Doctype wie <!doctype html>, damit Browser sie im Standardmodus statt im Quirks-Modus darstellen. Ergänzen Sie xsl:output um doctype-system="about:legacy-compat", die Legacy-Zeichenkette, die der HTML-Standard für Werkzeuge vorsieht, die den kurzen Doctype nicht ausgeben können. Damit schrieb lxml <!DOCTYPE html>. Das Attribut indent="yes" erlaubt dem Prozessor nur, Leerraum einzufügen, wie es ein XML-Formatter tun würde: libxslt setzte Zeilenumbrüche zwischen die meisten Elemente, rückte aber nichts ein.
Wie führe ich XSLT in PHP, Python oder Node.js aus?
Die XSL-Erweiterung von PHP und lxml in Python bauen beide auf libxslt auf, derselben Bibliothek, mit der laut Chromes Ankündigung auch die Chromium-Browser XSLT verarbeiten. Node.js braucht ein Paket.
Chrome begründet das Aus für XSLT so: Bibliotheken wie libxslt seien komplexer, alter C/C++-Code und anfällig für Speichersicherheitslücken. Auf Ihrem Server bestimmen Sie selbst, was verarbeitet wird: Wenden Sie nur Ihre eigenen Stylesheets auf Ihr eigenes XML an, nie ein Stylesheet, das von Nutzern stammt, und halten Sie libxslt mit den Systemupdates aktuell.
PHP
Nutzen Sie die Klasse XSLTProcessor aus der XSL-Erweiterung von PHP. Laut PHP-Handbuch ist die Erweiterung in PHP enthalten und wird beim Kompilieren mit der Option --with-xsl aktiviert. Prüfen Sie deshalb zuerst, ob php -m den Eintrag xsl aufführt. Bei unserem PHP 8.3 aus dem offiziellen WordPress-Docker-Image war das nicht der Fall, und die Klasse XSLTProcessor fehlte.
<?php
$xml = new DOMDocument();
$xml->load('sitemap.xml');
$xsl = new DOMDocument();
$xsl->load('sitemap.xsl');
$proc = new XSLTProcessor();
$proc->importStylesheet($xsl);
$html = $proc->transformToXml($xml);
if ($html === false) {
http_response_code(500);
exit;
}
header('Content-Type: text/html; charset=utf-8');
echo $html;
transformToXml gibt das Ergebnis als String zurück, bei einem Fehler false; deshalb prüft der Code den Rückgabewert, bevor er einen Header sendet. Da die XSL-Erweiterung fehlte, beruht dieses Beispiel auf dem Handbuch und wurde nicht ausgeführt.
Python
Installieren Sie lxml mit pip install lxml. Laut seiner Dokumentation unterstützt lxml XSLT 1.0 über libxml2 und libxslt.
from lxml import etree
doc = etree.parse("sitemap.xml")
transform = etree.XSLT(etree.parse("sitemap.xsl"))
result = transform(doc)
html = str(result)
Das Ergebnis wird so serialisiert, wie es xsl:output im Stylesheet festlegt. Damit str(result) einen Python-String liefert, sollte xsl:output laut der lxml-Dokumentation als Codierung UTF-8 angeben, wie in unserem Stylesheet. Wir haben das mit Python 3.14.3 und lxml 6.0.2 ausgeführt: Heraus kam die HTML-Tabelle mit zwei Zeilen und einer leeren Datumszelle für die zweite Seite.
Node.js
Node.js hat unter seinen eingebauten Modulen keinen XSLT-Prozessor. Wir haben zwei Pakete getestet.
SaxonJS. Das Paket saxon-js von Saxonica ist ein XSLT-3.0-Prozessor für Node.js. XSLT 3.0 definiert einen Kompatibilitätsmodus für Stylesheets mit version="1.0", und unser Stylesheet lief unverändert. SaxonJS.transform() erwartet ein kompiliertes Stylesheet, eine SEF-Datei. Kompilieren Sie es deshalb einmal mit dem Befehl xslt3:
npm install saxon-js xslt3
npx xslt3 -xsl:sitemap.xsl -export:sitemap.sef.json -nogo
Führen Sie die Umwandlung dann in einem ES-Modul aus, etwa in einer Datei namens transform.mjs:
import SaxonJS from 'saxon-js';
const output = await SaxonJS.transform({
stylesheetFileName: 'sitemap.sef.json',
sourceFileName: 'sitemap.xml',
destination: 'serialized',
}, 'async');
const html = output.principalResult;
Laut seinem README ist SaxonJS kostenlos, die Lizenz ist aber keine Open-Source-Lizenz.
xslt-processor. Dieses Paket ist in JavaScript geschrieben, ohne native Abhängigkeiten. Sein README zeigt den Aufruf: Beide Dateien werden mit der Klasse XmlParser geparst und dann an die Methode xsltProcess eines Xslt-Objekts übergeben, die ein Promise mit dem Ergebnis zurückgibt. Dort steht auch, dass einige von den Standards verlangte Funktionen fehlen und dass das Paket XSLT-Elemente am Präfix xsl: erkennt, ohne Namespace-Deklarationen zu beachten. Unser Beispiel lief damit; testen Sie Ihre eigenen Stylesheets, bevor Sie sich darauf verlassen.
Wann reicht das Skript xslt-polyfill?
Wenn Sie die XML-Dateien ändern können, aber nicht den Code, der sie ausliefert. Das Polyfill wendet das Stylesheet im Browser mit JavaScript an. Das XSL bleibt unverändert, das XML bekommt nur eine zusätzliche Skriptzeile. Wie diese aussieht, zeigt unsere Seite zum Ende von XSLT in Chrome. Das README von xslt-polyfill nennt drei praktische Punkte:
- Das Skript ist auf npm als
xslt-polyfillveröffentlicht, und Sie liefern die Datei von Ihrer eigenen Website aus. - Es wendet nur ein Stylesheet an, auf das das Dokument bereits mit einer
xml-stylesheet-Zeile verweist. - Solange das browsereigene XSLT aktiv ist, wandelt der Browser das Dokument um, bevor ein Skript läuft. Testen Sie das Polyfill daher mit abgeschaltetem XSLT unter
chrome://flags/#xslt.
Auch auf den Content-Type kommt es an. In unserem Test mit abgeschaltetem XSLT lief ein Script-Element in einem RSS-Feed, solange der Feed als application/xml kam. Kam er als application/rss+xml, zeigte Chrome ihn als reinen Text, und das Skript lief nicht. Laut Chrome sollte diese Skriptzeile Feedreader nicht daran hindern, das XML eines Feeds zu lesen. Googles Dokumentation zu Sitemaps äußert sich dazu nicht. Prüfen Sie daher den Sitemaps-Bericht der Search Console, nachdem Sie die Zeile in eine Sitemap eingefügt haben.
Dafür läuft bei jedem Besucher erst ein Skript, bevor die Seite erscheint; bei der Umwandlung auf dem Server kommt gleich fertiges HTML an.
Mit welchem Content-Type sende ich das erzeugte HTML?
Mit text/html, dem Medientyp, den MDN für HTML-Dokumente angibt. Senden Sie also Content-Type: text/html; charset=utf-8, wie im PHP-Beispiel. Die Seite funktioniert dann in jedem Browser, mit oder ohne XSLT.
Besucher, die auf eine nicht umgewandelte Seite stoßen, können sich selbst helfen: schon heute mit der offiziellen Erweiterung XSLT Polyfill oder mit XMLNest, einem XML-Viewer für Chrome, sobald es im Chrome Web Store erscheint. Wie XMLNest entstanden ist, erzählt der Beitrag Warum ich XMLNest entwickelt habe.
Quellen
- Chrome for Developers: Removing XSLT for a more secure browser (geprüft am 14. Oktober 2026)
- Chrome for Developers: Get started with origin trials (geprüft am 14. Oktober 2026)
- XSLT Polyfill, README von mfreed7/xslt_polyfill auf GitHub (geprüft am 14. Oktober 2026)
- W3C: XSL Transformations (XSLT) Version 1.0 (geprüft am 14. Oktober 2026)
- W3C: XML Path Language Version 1.0, Node Tests (geprüft am 14. Oktober 2026)
- W3C: XSL Transformations (XSLT) Version 3.0, XSLT 1.0 Compatibility Mode (geprüft am 14. Oktober 2026)
- HTML Standard: The DOCTYPE (geprüft am 14. Oktober 2026)
- MDN: Quirks-Modus und Standardmodus (geprüft am 14. Oktober 2026)
- PHP-Handbuch: XSLTProcessor::transformToXml (geprüft am 14. Oktober 2026)
- PHP-Handbuch: XSL-Erweiterung, Installation (geprüft am 14. Oktober 2026)
- PHP-Handbuch: Kommandozeilenoptionen (geprüft am 14. Oktober 2026)
- lxml: XSLT (geprüft am 14. Oktober 2026)
- Node.js-Dokumentation: API-Übersicht (geprüft am 14. Oktober 2026)
- npm: saxon-js (geprüft am 14. Oktober 2026)
- npm: xslt-processor (geprüft am 14. Oktober 2026)
- Google Search Central: Sitemap erstellen und einreichen (geprüft am 14. Oktober 2026)
- MDN: Gängige Medientypen (geprüft am 14. Oktober 2026)
- Test von XMLNest : Ausgeführt von XMLNest am 14. Oktober 2026 mit den Beispieldateien dieses Artikels: Python 3.14.3 mit lxml 6.0.2 (libxslt 1.1.39) erzeugte die HTML-Tabelle mit zwei Zeilen, beginnend mit <html><body> ohne DOCTYPE, und mit <!DOCTYPE html>, nachdem doctype-system="about:legacy-compat" ergänzt war; Node.js 24.20.0 mit saxon-js 2.7.0 und xslt3 2.7.0 sowie mit xslt-processor 5.1.2 erzeugte dieselbe Tabelle. In Chromium 156.0.8078.4 mit abgeschalteter XSLT-Funktion lief ein Script-Element im XHTML-Namespace in einem RSS-Feed, wenn der Feed als application/xml gesendet wurde, nicht aber als application/rss+xml. PHP 8.3.35 im offiziellen Docker-Image wordpress:7.1.3-php8.3-apache hatte keine XSL-Erweiterung; das PHP-Beispiel folgt daher dem PHP-Handbuch und wurde nicht ausgeführt (geprüft am 14. Oktober 2026)