Chrome is removing XSLT
Chrome is removing XSLT from the browser. Not a rumour, not a proposal: a dated deprecation with a Chromium tracking bug, an intent-to-deprecate thread on blink-dev and a migration guide on developer.chrome.com. Two things go away, the XSLTProcessor JavaScript class and the <?xml-stylesheet type="text/xsl"?> processing instruction, and both stop working on Chrome stable on 17 November 2026.
Console warnings have been running since Chrome 142 in October 2025, so if you are affected you may already have seen one. Below is the full timeline, why Google is doing it, who actually breaks, and the four migration paths with their real costs.
#The published timeline
Chrome ships on a roughly four-week stable cycle, and the removal is staged across seven milestones spanning almost two years. These dates are from Chrome's own deprecation document and the Chromium tracking bug, issue 435623334.
| Milestone | Date | What happens |
|---|---|---|
| Chrome 142 | 28 October 2025 | Early-warning console messages |
| Chrome 143 | 2 December 2025 | Official deprecation: console plus Lighthouse warnings |
| Chrome 145 | Canary, Dev, Beta | XSLT disabled by default on pre-stable channels |
| Chrome 146 | 10 March 2026 | Enterprise policy available as an opt-out |
| Chrome 152 | 25 August 2026 | Origin trial opens |
| Chrome 158 | 17 November 2026 | XSLT stops working on stable, except under trial or policy |
| Chrome 176 | 17 August 2027 | Trial and policy both expire: full removal |
Both escape hatches expire together at Chrome 176, so neither is a solution, only a deferral. The enterprise policy is also not available to you as a public site operator: an administrator sets it on managed devices, which helps an internal intranet and does nothing for a podcast feed read by strangers.
#Why they are doing it
The proposal began as whatwg/html issue 11523, "Should we remove XSLT from the web platform?", opened by Mason Freed of Google on 1 August 2025. The argument has two halves and both are stronger than the usual "nobody uses it".
The first is attack surface. Chrome and Safari do not implement XSLT themselves; they embed libxslt, a C library from the early 2000s that sits in the renderer and parses attacker-controlled input. It has an unhappy security history, and in June 2025 its long-standing maintainer stepped down, leaving a memory-unsafe C dependency in the most exposed process of the browser with no clear maintenance story. Firefox is in a different but not better position: TransforMiiX is Mozilla's own C++ code, also old, also memory-unsafe.
The second is usage. Freed's measurements put pages using the xml-stylesheet mechanism at roughly 0.001 per cent of page loads, quoted in reporting as one load in 7,891. For a feature carrying that much risk, that is a hard ratio to defend. The counter-argument, in whatwg/html issues 11582 and 12103, is that the percentage understates institutional use: the affected sites skew heavily towards government, regulatory and library systems, where traffic is low, budgets are fixed and nobody has touched the publishing pipeline in fifteen years.
Be precise about the claim. Nobody is arguing that XSLT is a bad language, only that shipping a large old C transformation engine inside every browser process is a poor trade at 0.001 per cent usage. Running XSLT on a server, or in WebAssembly, is untouched.
#Who actually breaks
The largest visible group is feeds. A great many RSS and Atom feeds, and most podcast feeds published through a self-hosted tool, carry a processing instruction so a human who clicks the feed link sees a styled page rather than a screenful of raw XML.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/feed.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Example Podcast</title>
<atom:link href="https://example.com/feed.xml" rel="self"
type="application/rss+xml"/>
</channel>
</rss>The failure is not an error page. From Chrome 158 the processing instruction is ignored and the browser falls back to its built-in XML tree viewer, so a visitor gets a collapsible view of the raw feed. Feed readers are unaffected: they parse the XML and never look at the stylesheet. What you lose is the human landing page, on the one URL people paste into a browser to see whether a podcast exists.
- Government and regulatory publishing. Legislation, court records, procurement notices and financial filings are frequently stored as XML with XSLT as the rendering layer, because the XML is the legal record and the presentation is deliberately separate from it.
- Library and archive catalogues. MARCXML, EAD finding aids, METS and OAI-PMH responses are commonly rendered client side by software that has been stable for a decade.
- Legacy intranets and enterprise reporting. SOAP-era internal tools that transform a service response into a page. This is the one group the enterprise policy genuinely helps, and only until August 2027.
Two checks cover almost everything. Grep your source for XSLTProcessor, for xml-stylesheet and for the string text/xsl. Then open the affected pages in Chrome and read the console, where the warning has been printed since Chrome 142. To see the actual post-removal behaviour today, open them in Canary or Dev, where XSLT has been off by default since Chrome 145.
#The four ways out
Chrome's migration guidance and the blink-dev thread point at four options. They are not equally good, and the right one depends mostly on whether you control a build step.
| Path | Keeps your stylesheet | Cost to the visitor | Best for |
|---|---|---|---|
| Transform on the server or at build time | Yes, unchanged | None: plain HTML arrives | Feeds, static sites, anything with a pipeline |
| JSON plus a client framework | No, rewrite | Framework payload plus a fetch | Applications being rewritten anyway |
| Saxon-JS in the page | Yes, and gains XSLT 3.0 | Runtime download plus a build-time compile | Client-side rendering you cannot move |
| WebAssembly libxslt polyfill | Yes, unchanged | About 520 KB gzipped, once | Keeping existing script working as-is |
Server-side or build-time transformation is the correct answer for the feed case and most others. The stylesheet you already have keeps working, unmodified, in xsltproc or Saxon, and the browser receives HTML that needs no engine at all. For a feed, serve the XML at its existing URL for readers and a pre-rendered HTML page for humans.
# libxslt's own command line tool, XSLT 1.0, almost certainly installed.
# --nonet stops it fetching anything the documents reference.
xsltproc --nonet -o public/feed.html feed.xsl public/feed.xml
# Saxon if you want XSLT 2.0 or 3.0 features while you are here.
java -jar saxon-he.jar -s:public/feed.xml -xsl:feed.xsl -o:public/feed.html
# Keep serving the XML at its original URL so no subscriber's reader
# breaks, and link the human page from your site.JSON plus a client framework is what people reach for reflexively and is usually the worst value. It discards a working declarative stylesheet, replaces it with imperative rendering code, and adds a JavaScript dependency to a page that had none, to solve a problem the server-side path solves for free. It makes sense only inside a rewrite you were doing anyway.
Saxon-JS from Saxonica is a JavaScript XSLT 3.0 processor, so it is the only path that gives you more than you had: xsl:function, xsl:for-each-group, regular expressions via xsl:analyze-string, maps and arrays, none of which any browser ever supported. The cost is a compile step, because Saxon-JS runs a stylesheet compiled ahead of time into a SEF file rather than reading your XSL directly, plus a real runtime download on every page that uses it.
The polyfill is libxslt compiled to WebAssembly, written by the same Chrome engineer who filed the removal intent and referenced from Chrome's own migration document. It reinstalls a global XSLTProcessor with the same API, so existing script keeps working, at around 520 KB gzipped. One caveat that is rarely stated: this is the same libxslt with the same bug history. What changes is the blast radius. A memory error inside a WebAssembly module corrupts that module's linear memory and cannot reach the host process, which is why moving it into the sandbox is an improvement even though the code is identical.
#Firefox and Safari
Neither Mozilla nor Apple has announced a removal or published a date. XSLTProcessor and the xml-stylesheet processing instruction keep working in Firefox and Safari after Chrome 158, and nothing in the Chrome schedule applies to them.
Do not read that as opposition. Mozilla has a tracking bug, 1990759, "Investigate deprecation and removal of XSLT", and its recorded standards position is positive with a compatibility concern. Chrome's document states that both other engines support the removal in principle. Chrome is going first and the others are watching what breaks.
One practical consequence of the split: between November 2026 and whenever the others move, the same page renders differently in different browsers with no error in either. A page that looks fine when you check it in Firefox may be showing a raw XML tree to most of your visitors. Test in Chrome.
#What this site did
The XSLT transformer here faced the same choice. Building on XSLTProcessor alone would mean shipping a tool that silently stops working for most of its users on a known date. Building on WebAssembly alone would mean charging every visitor around 520 KB for a capability their browser already has.
It does neither: native first, WebAssembly as a fallback, and the result panel says which engine ran. Before transforming, it runs a real one-element transformation to check the native processor is not just present but working, because a constructor that exists and fails is worse than one that is absent: the failure would otherwise look like a bug in your stylesheet. If the check fails, libxslt compiled to WebAssembly is fetched once and the transformation proceeds with a note explaining why.
export function nativeXsltAvailable(): boolean {
if (typeof XSLTProcessor === 'undefined' || typeof DOMParser === 'undefined') return false;
try {
const parser = new DOMParser();
const xsl = parser.parseFromString(
'<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">' +
'<xsl:template match="/"><ok/></xsl:template></xsl:stylesheet>',
'application/xml',
);
const p = new XSLTProcessor();
p.importStylesheet(xsl);
const out = p.transformToDocument(parser.parseFromString('<a/>', 'application/xml'));
return out?.documentElement?.nodeName === 'ok';
} catch {
return false;
}
}Two limitations survive the fallback and are stated in the tool rather than hidden. Both engines are XSLT 1.0, so a stylesheet declaring version="2.0" is flagged before it runs: one that half-works is harder to debug than one that refuses. And because everything runs in the tab, document() and remote xsl:include cannot fetch across origins. Neither changes on 17 November 2026. Only the engine changes, and nothing leaves the browser either way.
Common questions
What exactly stops working, and when?
The XSLTProcessor JavaScript class and the <?xml-stylesheet type="text/xsl"?> processing instruction, both on Chrome stable in Chrome 158, dated 17 November 2026. An origin trial and an enterprise policy can extend that, and both expire in Chrome 176 on 17 August 2027, after which the code is gone.
<?xml-stylesheet type="text/css"?> is a different feature and is not affected.
My podcast feed uses an XSL stylesheet. What is the smallest fix?
Transform the feed to HTML in your build or on your server and serve that as a normal page. Your existing stylesheet works unchanged in xsltproc or Saxon, so this is an addition to a build script rather than a rewrite. Keep serving the XML at its existing URL: feed readers never looked at the stylesheet and must not be disturbed.
If you have no build step at all, replacing type="text/xsl" with a CSS stylesheet gets you a readable page with no engine involved. It gives far less control than XSLT did, but <?xml-stylesheet type="text/css"?> is not being removed and needs no toolchain.
Will the WebAssembly polyfill keep my page working after Chrome 158?
For script that calls XSLTProcessor, yes. The polyfill installs a global XSLTProcessor with the same interface, backed by libxslt compiled to WebAssembly, so existing code keeps running at the cost of roughly 520 KB gzipped and a load delay on first use.
It does not restore the declarative path. Nothing can polyfill <?xml-stylesheet type="text/xsl"?>, because the browser must act on that instruction before any of your JavaScript exists. If a raw XML URL is what people visit, you need a real page at that URL instead.
Should I switch to Saxon-JS or to the polyfill?
The polyfill if the goal is to keep something working with minimal change: it is a drop-in for the same API, runs the same XSLT 1.0 stylesheets, and nothing else in your code moves.
Saxon-JS if you are spending the migration anyway and want something back for it. It is XSLT 3.0, which no browser ever shipped, so xsl:function, xsl:for-each-group and xsl:analyze-string become available. The price is that stylesheets must be compiled to a SEF file ahead of time, so you need a build step, plus a per-page runtime download.
If you have a server or a static build, neither is the best answer. Transform there and send HTML.
Are Firefox and Safari removing XSLT too?
Neither has announced a removal or published a date, so XSLT keeps working in both after Chrome 158.
Neither is defending it either. Mozilla has an open tracking bug, 1990759, and a recorded standards position that is positive with a compatibility concern, and Chrome's deprecation document says both other engines support removing the feature. Treat Firefox and Safari as extra time to migrate, not as a reason not to.
Sources
- Chrome for Developers: Removing XSLT for a more secure browser
- blink-dev: Intent to Deprecate and Remove XSLT
- Chromium issue 435623334: Deprecate and remove XSLT
- whatwg/html issue 11523: Should we remove XSLT from the web platform?
- Mozilla bug 1990759: Investigate deprecation and removal of XSLT
- W3C XSL Transformations (XSLT) Version 1.0