Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions public/_headers
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion public/_routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"/search-overlay.js",
"/spec-toc.js",
"/admin-stats.js",
"/sitemap.xsl",
"/okf/*",
"/okf.tar.gz"
]
Expand Down
260 changes: 0 additions & 260 deletions public/sitemap.xsl

This file was deleted.

8 changes: 8 additions & 0 deletions src/content/changelog/2026-09-10-xslt-sitemap-stylesheets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: "XSL sitemap stylesheets are out"
date: "2026-09-10"
type: changed
relatedSlugs: [xml-sitemaps]
---

[XML sitemaps](/spec/seo/xml-sitemaps/) used to recommend an `<?xml-stylesheet?>` 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 schedules its removal for version 158 with temporary exceptions, and Firefox and WebKit also support removal — so the page recommends an ordinary HTML index instead, and this site has dropped the processing instruction from its own sitemaps.
32 changes: 13 additions & 19 deletions src/content/spec/seo/xml-sitemaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ 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-10T00:00:00.000Z"
sources:
- title: "Sitemaps XML format"
url: "https://www.sitemaps.org/protocol.html"
publisher: "sitemaps.org"
- 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
Expand Down Expand Up @@ -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 `<lastmod>` 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 `<?xml-stylesheet?>` 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 `<?xml-stylesheet?>` processing instruction immediately after the XML declaration, before the `<urlset>` (or `<sitemapindex>`) 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 plans to disable native XSLT in version 158, scheduled for 17 November 2026, with temporary exceptions for origin-trial and enterprise-policy participants until Chrome 176, scheduled for 17 August 2027. Firefox and WebKit have also signalled removal intent; they do not share Chrome's published timetable.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/sitemap.xsl"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
...
</urlset>
```
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 `<?xml-stylesheet?>` 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 `<urlset>` and `<sitemapindex>` 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.
XML itself and XML styled with CSS remain supported; this removal concerns native XSLT transformations. 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

Expand Down
5 changes: 2 additions & 3 deletions src/lib/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ export function clampLastmod(d: string | undefined): string | undefined {
}

const XML_DECL = '<?xml version="1.0" encoding="UTF-8"?>';
const STYLESHEET_PI = '<?xml-stylesheet type="text/xsl" href="/sitemap.xsl"?>';

export function renderUrlset(entries: SitemapEntry[]): string {
const lines: string[] = [XML_DECL, STYLESHEET_PI];
const lines: string[] = [XML_DECL];
lines.push('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
for (const e of entries) {
lines.push(" <url>");
Expand All @@ -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(
'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
);
Expand Down
Loading