Blog

How Mosaic Set the Template for User Agent Strings

Every user agent string in use today follows a shape laid down before the web had a commercial browser market. The format is a convention rather than a requirement, and it has proved almost impossible to change.

The original shape was product and version

The earliest graphical browsers identified themselves with a product name, a slash, and a version number. That was the whole string, and it was enough because there were few browsers and fewer differences between them.

Server operators used the value for statistics and occasionally for serving a different page. Nothing about the format was designed for the kind of parsing that would later depend on it.

The simplicity was possible because identity was unambiguous. One product name mapped to one piece of software, so no parser needed to interpret anything beyond splitting on a slash.

Parenthesised comments carried the platform

Operating system and hardware details went into a parenthesised comment following the version. This was the escape hatch for anything that did not fit the product-and-version pattern.

Because the comment had no internal grammar, its contents varied by build. Some included the processor architecture, others the window system, others a locale code that meant little outside the vendor.

That freedom is why the comment section became the messiest part of the string. Everything a vendor wanted to communicate but could not express as a product token ended up inside the brackets.

The specification described it after the fact

HTTP standardised the header by describing what browsers already did rather than by prescribing a grammar. Product tokens separated by whitespace, each optionally followed by a comment, was the rule.

Describing existing practice kept the specification honest but locked in the ambiguity. A parser that follows the grammar exactly can split a string into tokens and still not know which token names the browser.

Later browsers exploited that gap. Once multiple product tokens were legal, a string could name several products at once without technically violating anything.

Compatibility pressure froze the format

Sites written against one browser began checking the string before serving content. Any browser that wanted those pages had to match what the checks expected, which meant copying tokens it did not own.

Each generation inherited the previous generation's tokens because removing one broke pages nobody was maintaining. Accumulation was always cheaper than negotiation with thousands of site owners.

The result is a string that reads as a layered archaeological record. Reading it left to right walks through the compatibility decisions of three decades rather than describing the software actually running.

The template survived its own justification

Modern browsers no longer need the original tokens for compatibility with software that has not existed for years. They keep them because nobody can prove which sites would break if they stopped.

Structured replacements express the same information in separate fields with defined types. The old header persists alongside them, which means parsers must handle both shapes for the foreseeable future.

The lesson generalises beyond this header. A format defined by what implementations happened to send, rather than by what consumers need, tends to accumulate rather than converge.