From fc32e5974f129c6c56d9d9b7fb956d9eeed467f5 Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Sat, 5 Sep 2026 08:08:30 +0200 Subject: [PATCH] change(xml-sitemaps): XSL stylesheets are out, the HTML Standard says avoid XSLT The HTML Standard gained an explicit warning against client-side XSLT on 2026-08-25 (whatwg/html@208004f): browser XSLT implementations are highly susceptible to memory-safety vulnerabilities and the feature is being removed from the web platform. Chrome stops running it in 158 (17 Nov 2026); Firefox and WebKit intend to follow. The page recommended exactly this, and claimed XSLT 1.0 was supported by current Chrome, Firefox and Safari. Rewrites the section to advise an ordinary HTML index instead, and drops the processing instruction from this site's own sitemaps so we no longer ship what we advise against. Co-Authored-By: Claude Opus 5 (1M context) --- public/_headers | 5 --- public/_routes.json | 1 - .../2026-09-05-xslt-sitemap-stylesheets.md | 8 +++++ src/content/spec/seo/xml-sitemaps.md | 32 ++++++++----------- src/lib/sitemap.ts | 5 ++- 5 files changed, 23 insertions(+), 28 deletions(-) create mode 100644 src/content/changelog/2026-09-05-xslt-sitemap-stylesheets.md diff --git a/public/_headers b/public/_headers index 8cad0829..31988d95 100644 --- a/public/_headers +++ b/public/_headers @@ -149,11 +149,6 @@ Content-Type: application/xml; charset=utf-8 Cache-Control: public, max-age=3600, stale-if-error=86400 -# Sitemap XSL stylesheet — styles the XML when viewed in a browser -/sitemap.xsl - Content-Type: application/xslt+xml; charset=utf-8 - Cache-Control: public, max-age=3600 - # RFC 9727 — Publishing Organisation API Information /.well-known/api-catalog Content-Type: application/linkset+json; charset=utf-8 diff --git a/public/_routes.json b/public/_routes.json index 9c6ff3e6..011ca2dc 100644 --- a/public/_routes.json +++ b/public/_routes.json @@ -19,7 +19,6 @@ "/search-overlay.js", "/spec-toc.js", "/admin-stats.js", - "/sitemap.xsl", "/okf/*", "/okf.tar.gz" ] diff --git a/src/content/changelog/2026-09-05-xslt-sitemap-stylesheets.md b/src/content/changelog/2026-09-05-xslt-sitemap-stylesheets.md new file mode 100644 index 00000000..c61493e3 --- /dev/null +++ b/src/content/changelog/2026-09-05-xslt-sitemap-stylesheets.md @@ -0,0 +1,8 @@ +--- +title: "XSL sitemap stylesheets are out" +date: "2026-09-05" +type: changed +relatedSlugs: [xml-sitemaps] +--- + +[XML sitemaps](/spec/seo/xml-sitemaps/) used to recommend an `` processing instruction so a browser would render the sitemap as a readable table. The HTML Standard now tells authors to avoid client-side XSLT altogether — Chrome stops running it in version 158, and Firefox and WebKit intend to follow — so the page recommends an ordinary HTML index instead, and this site has dropped the processing instruction from its own sitemaps. diff --git a/src/content/spec/seo/xml-sitemaps.md b/src/content/spec/seo/xml-sitemaps.md index 9db230ae..a06939b7 100644 --- a/src/content/spec/seo/xml-sitemaps.md +++ b/src/content/spec/seo/xml-sitemaps.md @@ -7,7 +7,7 @@ status: recommended order: 20 appliesTo: [all] relatedSlugs: [sitemap-index, image-sitemaps, robots-txt, canonical-url, schemamap] -updated: "2026-07-09T00:00:00.000Z" +updated: "2026-09-05T00:00:00.000Z" sources: - title: "Sitemaps XML format" url: "https://www.sitemaps.org/protocol.html" @@ -15,12 +15,12 @@ sources: - title: "Build and submit a sitemap" url: "https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap" publisher: "Google Search Central" - - title: "XSL Transformations (XSLT) Version 1.0" - url: "https://www.w3.org/TR/xslt-10/" - publisher: "W3C" - - title: "XML sitemaps: the most important SEO tool" - url: "https://yoast.com/what-is-an-xml-sitemap-and-why-should-you-have-one/" - publisher: "Yoast" + - title: "HTML Standard — Interactions with XPath and XSLT" + url: "https://html.spec.whatwg.org/multipage/infrastructure.html#interactions-with-xpath-and-xslt" + publisher: "WHATWG" + - title: "Removing XSLT for a more secure browser" + url: "https://developer.chrome.com/docs/web-platform/deprecating-xslt" + publisher: "Chrome for Developers" --- ## What it is @@ -65,23 +65,17 @@ Generate sitemaps dynamically from your content source, not by crawling your own **This site ships it.** `specification.website` generates [`/sitemap-index.xml`](/sitemap-index.xml) at build time from the content collection, and sets each `` from the entry's `updated` front matter — the same field the [RSS feed](/rss.xml) uses — rather than the build timestamp, so the date only moves when the content actually changes. -## A stylesheet for human readers +## Do not attach an XSL stylesheet -Browsers parse XML, but the raw view is hostile to anyone who is not a crawler. An [XSL stylesheet](https://www.w3.org/TR/xslt-10/) referenced from the sitemap transforms it into HTML in the browser, so a person who opens the URL sees a readable page with clickable links. Crawlers ignore the stylesheet and parse the underlying XML directly. +A sitemap opened in a browser shows raw XML, which is hostile to anyone who is not a crawler. The long-standing fix was an `` processing instruction pointing at an XSLT stylesheet, which the browser applied to render the sitemap as a readable HTML table. That advice has expired. -Reference the stylesheet with an `` processing instruction immediately after the XML declaration, before the `` (or ``) root element: +Since August 2026 the [HTML Standard](https://html.spec.whatwg.org/multipage/infrastructure.html#interactions-with-xpath-and-xslt) tells authors to avoid client-side XSLT outright: browser XSLT implementations are, in its words, highly susceptible to memory-safety vulnerabilities, and the feature is being removed from the web platform. Chrome stops running it in version 158, due 17 November 2026; Firefox and WebKit have signalled the same intent. This is a removal, not a deprecation warning — the stylesheet simply stops being applied. -```xml - - - - ... - -``` +The failure is worth understanding precisely, because it is milder than it sounds and that is exactly why it gets left in place too long. Crawlers never read the stylesheet; they parse the XML underneath it, and an `` instruction a parser cannot process is ignored rather than fatal. So nothing about discovery or indexing breaks. What breaks is the human view: a URL that rendered a tidy table starts rendering the browser's raw-XML fallback, and nobody notices until someone opens the sitemap and reports it as a bug. -The same stylesheet can render both `` and `` documents by matching on the root element. Serve the `.xsl` file as `application/xslt+xml` from the same origin as the sitemap; cross-origin XSL is blocked by browsers. XSLT 1.0 is supported by current Chrome, Firefox, and Safari with no client-side dependencies. +If you want a page a person can read, write one in HTML and link it. It costs no more than the stylesheet did, it is crawlable and linkable in its own right, and it does not depend on a feature three engines are deleting. -**This site ships it.** Open [`/sitemap-index.xml`](/sitemap-index.xml) or any per-category sitemap in a browser to see the transformed view. The stylesheet lives at [`/sitemap.xsl`](/sitemap.xsl). +**This site no longer does it.** `specification.website` pointed its sitemaps at a `/sitemap.xsl` until this page changed; the processing instruction has been dropped rather than left to break. ## Common mistakes diff --git a/src/lib/sitemap.ts b/src/lib/sitemap.ts index 5df73487..da4f16b8 100644 --- a/src/lib/sitemap.ts +++ b/src/lib/sitemap.ts @@ -37,10 +37,9 @@ export function clampLastmod(d: string | undefined): string | undefined { } const XML_DECL = ''; -const STYLESHEET_PI = ''; export function renderUrlset(entries: SitemapEntry[]): string { - const lines: string[] = [XML_DECL, STYLESHEET_PI]; + const lines: string[] = [XML_DECL]; lines.push(''); for (const e of entries) { lines.push(" "); @@ -54,7 +53,7 @@ export function renderUrlset(entries: SitemapEntry[]): string { } export function renderSitemapIndex(entries: SitemapEntry[]): string { - const lines: string[] = [XML_DECL, STYLESHEET_PI]; + const lines: string[] = [XML_DECL]; lines.push( '', );