HTML file format
HyperText Markup Language, the structure format of web pages.
- MIME type
- text/html
- Extensions
- .html, .htm
- Category
- Text & markup
Magic-number signature
Plain text starting with '<!doctype html' or '<html'.
About the HTML format
HTML is defined by its parser rather than by a grammar. The standard does not describe a set of legal documents and reject everything else; it describes, byte by byte, what a browser must do with any input at all, including input that is malformed, truncated or not HTML in the first place. That is why there is no such thing as an invalid HTML file in practice, why two different browsers build identical trees out of identically broken markup, and why the format has no header, no length, no version and no trailer that a tool could check before it starts parsing.
Header layout, byte by byte
| Offset | Length | Field | Meaning |
|---|---|---|---|
| 0 | 3 | UTF-8 byte-order mark | EF BB BF. When it is present the browser stops looking: the standard says a mark makes the encoding certain, which overrides both the charset in the Content-Type header and any meta element inside the document. It is one of the very few things in HTML that is decided at a fixed byte offset. |
| 0 | 15 | DOCTYPE | 3C 21 44 4F 43 54 59 50 45 20 68 74 6D 6C 3E, the fifteen bytes of the modern doctype. Case does not matter, and the browser sniffing rules only require the first fourteen of them - the letters up to and including HTML - followed by a space or a closing angle bracket. |
| 0 | 5 | html start tag | 3C 68 74 6D 6C, plus a space or a closing bracket after it. Perfectly legal at offset 0, because the doctype is not mandatory; a document without one is parsed in quirks mode, where a set of decades-old layout behaviours comes back and box sizing stops matching what was designed. |
| 0 | variable | Any other recognised opener | The sniffing table also matches HEAD, SCRIPT, IFRAME, H1, DIV, FONT, TABLE, A, STYLE, TITLE, B, BODY, BR, P and a comment opener, each after optional leading whitespace and each needing a space or a closing bracket after the name. A fragment saved out of a page usually matches one of these rather than either of the first two rows. |
| 0 | 1024 | Encoding prescan window | Not a field but a boundary worth carrying in your head: browsers are told to look for a meta charset only within the first 1024 bytes. A charset declaration pushed past that by a long licence comment or an inlined script is simply not seen, and the page falls back to guessing. |
Telling it apart from its neighbours
| Easily confused with | How to tell |
|---|---|
| XML | A document that opens 3C 3F 78 6D is XML, and XHTML delivered as application/xhtml+xml goes to the XML parser, where one unclosed tag is fatal. The identical bytes delivered as text/html are recovered silently and render fine. The difference is not in the file at all; it is in the header the server sent, which is why the same document can behave two completely different ways from one directory. |
| SVG | A standalone .svg is XML with an svg root element and is parsed by the XML parser. The same markup written inline inside an HTML document is parsed by the HTML parser under foreign-content rules, where tag names are case-fixed and self-closing syntax is handled specially. Byte for byte the elements can be identical; which parser sees them decides what they mean. |
| Plain text/Markdown | There is no reliable byte test. If none of the tag patterns match, the sniffing algorithm falls back to plain text so long as the leading bytes contain no binary data, so a README, a template that opens with a variable, or an email body is indistinguishable from prose at the byte level. Extension and Content-Type are all you have, and both are assertions rather than evidence. |
| Failed downloads | The most useful thing to know about HTML in a hex viewer is that it is what a broken download usually turns out to be. A ZIP that will not open, an image that will not load, a PDF a reader rejects: open it and find 3C 21 44 4F 43 54 59 50 45 at offset 0 and you are looking at a login page, a rate-limit notice or a 404 saved under the name of the file you wanted. |
How these files break
- Something in front of the doctype. Whitespace and comments are ignored and cost nothing, but any actual text ahead of it - a warning emitted by a server-side language, a stray line from an included file - drops the document into quirks mode. The page still renders, because it always renders, with box sizing and inherited font sizes subtly different from the layout that was tested.
- No encoding declared anywhere. With no mark, no charset on the Content-Type and no meta within the first 1024 bytes, the browser guesses, and the guess depends on the reader's locale. The page is correct in the office it was built in and full of mojibake somewhere else, from bytes that never changed at any point.
- Truncation. A partial download has no tell at all: the parser closes every element still open when the input ends and renders what it has. A page missing its footer looks like a template bug rather than a file that stopped early, and the only way to be sure is to compare the byte count against the Content-Length that was advertised.
- Double escaping. Text escaped twice on its way through a template renders as visible tag syntax instead of formatting, and the file is completely well-formed while it does. The bytes to search for are 26 61 6D 70 3B - an escaped ampersand - sitting in front of an escaped angle bracket.
- A saved page that is not the page. Save-as rewrites URLs, relocates or inlines resources and injects a comment recording where the file came from, so the bytes on disk are not the bytes the server sent. Diffing a saved page against a fresh fetch mostly reports the browser's own edits, which makes saved copies close to useless as evidence.
Where the format stands today
HTML is not going to be replaced, so the interesting question about it is where it turns up outside a browser. Email is the largest case, and it runs on a fossil subset - table layouts and inline styles - because mail clients never adopted the parsing standard and never will. Electron and its imitators put a browser engine inside desktop applications, which means a large share of the software people use every day is HTML at the surface. Static site generators, documentation tools and design systems all emit it as a build product rather than as something anybody writes by hand any more. And as a file on disk it is most often met by accident: HTML is what you find when you open something that was supposed to be a different format and the server sent an error page instead, which makes recognising its first fifteen bytes one of the more useful things a person working with files can memorise.
Open HTML files on this site
Every one of these runs in your browser — the file never leaves your machine.
Related formats
Formats that are easy to confuse with HTML — compare their leading bytes.