XHTML failed for exactly one reason – Internet Explorer, with overwhelming market share, did not support it. If you served it as text/html then it would parse it, but not apply any of the XHTML parsing rules, it would just treat it as weird HTML.

The history of the web is littered with copious examples of what the article talks about. Here’s one example: HTTP has a Content-Type header that indicates the media type being transferred. Sometimes, web developers would screw up and serve things with the wrong media type. For instance, serving CSS files as text/plain instead of text/css.

Now, if no action were taken by browser vendors, the resolution would be simple: the developer would see that the styling on their site was completely broken, and fix it. But Internet Explorer decided to be “liberal in what it accepted”, and instead of respecting the Content-Type header, they used content sniffing to guess. This meant that when a web developer screwed up, and they only tested on the 90%+ market share Internet Explorer, they would think everything was fine but it would be broken in every browser but Internet Explorer.

After a while, so many sites were broken in Firefox that Firefox had to follow the non-standard behaviour as well. Leaving other minority browsers broken. At this point, the behaviour a browser needs to implement to render a website successfully is not what is written in the specifications, but copying unpredictable guessing from proprietary code. You can see how that’s harmful to interoperability, right? Even though interoperability is the entire purpose of Postel’s Law?

It gets worse though, because this content sniffing was happening all over, not just to CSS. So if a website said that something was a PNG image, if that image’s metadata contained the wrong sequence of bytes, it would get interpreted as an HTML document. This is important because PNGs don’t contain any kind of scripting, while HTML documents do.

So this opened up an attack vector: make a polyglot file that contains malicious JavaScript, and upload it as an image to a website. Then link to that image, and Internet Explorer users will execute the JavaScript in the security context of that website, resulting in a successful XSS attack. Even though the website served it as an image, not HTML, Internet Explorer would “be liberal” and do its best to interpret it as badly-broken HTML.

At some point, Internet Explorer came up with an authoritative: true parameter that opted out of this behaviour. Sometimes. Then they tried again with X-Content-Type-Options: nosniff. Meanwhile, web developers who just wanted to serve user-provided images without opening themselves up to attack vectors needed to follow all these twists and turns instead of just reading the spec. Ever wonder why opening an image URL on so many sites triggers a download? It’s because for many years it was the only safe way to avoid an image being interpreted as HTML.

This has happened over and over and over and over again on the web. Did you know that Netscape Navigator would interpret Unicode characters that merely look like angle brackets as if they were actually angle brackets? Consider the impact on security that one had. Remember the “samy is my hero” MySpace worm? That was down to browsers “helpfully” ignoring newlines in the javascript pseudo-protocol.

Postel’s law has been downright poisonous for security and interoperability. We would all be much better off with strict parsers.