MHTML File Documentation
Summary
An MHTML file (short for MIME HTML) is a complete web page saved as one file: the HTML plus every image, stylesheet and script it references, bundled together as MIME parts. It uses the same MIME multipart encoding as email, defined by RFC 2557 (1999), and .mhtml is the same format as .mht. Its MIME type is multipart/related (files are also served as message/rfc822). Chrome and Edge open one directly.
Technical details
| Feature | Value |
|---|---|
| Full name | MIME HTML (MHTML) web page archive |
| File extension | .mhtml, .mht |
| MIME type | multipart/related; also served as message/rfc822 and application/x-mimearchive |
| Format type | Text — MIME multipart message (RFC 822 style) |
| Purpose | Single-file web archive: HTML plus its embedded resources |
| Standard body | IETF (Internet Engineering Task Force) |
| Introduced | 1999 |
| Standard | RFC 2557 (obsoletes the experimental RFC 2110) |
| Related specs | RFC 2045/2046 (MIME), RFC 2392 (cid: URLs) |
| Open standard | Yes — royalty-free IETF Proposed Standard |
| Container / base | MIME multipart over the RFC 822 message format |
| Transfer encoding | base64 or quoted-printable per part |
| Root part type | text/html (the main document) |
| Resource linking | Content-Location URLs and cid: / Content-ID references |
| Byte order | N/A — plain text, no binary fields |
| Magic number | None; conventionally starts with From: or MIME-Version: 1.0 |
| Created by | Internet Explorer, Microsoft Edge, Chrome/Chromium, Opera, Microsoft Word |
| Apple counterpart | Safari .webarchive (proprietary) |
| Related extensions | .mht, .eml, .html, .htm, .webarchive |
| Specification | rfc-editor.org/rfc/rfc2557 |
What is an MHTML file?
MHTML is short for MIME HTML, and a .mhtml file is a single document that holds an entire web page: the HTML source plus every image, stylesheet and script the page pulls in. Where a page saved as plain HTML leaves its resources as loose files in a folder beside it, an MHTML file packs all of them into one container so the page travels as a self-contained unit. The two extensions .mhtml and .mht name the identical format; .mht is the shorter spelling that Internet Explorer used, and either can be renamed to the other without touching the bytes.
The format is an open IETF standard, RFC 2557, published in March 1999 as “MIME Encapsulation of Aggregate Documents, such as HTML (MHTML)”. It replaced the earlier experimental RFC 2110. The trick RFC 2557 pulls is to reuse the MIME multipart machinery already defined for email in RFC 2045 and RFC 2046: the page becomes a MIME message whose root part is the HTML and whose remaining parts are the resources. That is why a raw .mhtml file looks exactly like a saved email, complete with From:, Subject: and MIME-Version headers. Everything below is about how that message is laid out and how a browser turns it back into a rendered page.
The RFC 822 header block
An MHTML file begins the way an RFC 822 message begins: a block of header lines, one per line as Field-Name: value, ended by a single blank line. Software that saves MHTML typically writes a few informational headers first, From:, Subject: (often the page title) and Date:, none of which affect rendering. The lines that matter are MIME-Version: 1.0, which marks the file as MIME, and the top-level Content-Type. Because these are ordinary text headers with no binary framing, an .mhtml file has no magic number: you can open it in any text editor and read the whole structure by eye.
A Content-Location header may also appear in this top block. RFC 2557 is explicit that a Content-Location placed on the outer multipart/related heading applies to the aggregate as a whole, not to the root HTML part, and it acts as the base URI against which relative references elsewhere in the file are resolved.
The multipart/related content type and its boundary
The header that defines the whole file is the top-level Content-Type: multipart/related. RFC 2557 uses multipart/related (from RFC 2387) rather than the more familiar multipart/mixed because the parts are not independent attachments: they are one compound object, an HTML document and the resources it depends on. Two parameters ride on that header.
Content-Type: multipart/related;
type="text/html";
boundary="----=_NextPart_000_0000_01D8ABCD.12345678"
The type parameter names the media type of the root part, text/html, so a reader knows which part to render first. The boundary parameter is a delimiter string chosen by the writer to be unique — it must not occur inside any part’s data. Each part in the file is introduced by a line consisting of two hyphens followed by that boundary string; the very last part is closed by two hyphens, the boundary, and two more hyphens. A parser splits the file purely on those delimiter lines, so it never has to understand the content of a part to find where the next one starts.
Selecting the root part
The first body part after the opening boundary is, by convention, the root: the text/html document that is the actual page. Its own headers declare Content-Type: text/html (usually with a charset), a Content-Transfer-Encoding, and a Content-Location giving the URL the page was originally fetched from. When a start parameter is present on the outer multipart/related header it names, by Content-ID, which part is the root; when it is absent the first part is taken as the root. The root part is special because it is the only one a browser renders directly. Every other part exists only to satisfy a reference made from inside this HTML.
Content-Location and URL resolution
The core problem MHTML solves is rewriting: the saved HTML still contains its original references, such as <img src="images/logo.png"> or <link href="https://example.com/site.css">, and those references must now resolve to parts sitting inside the same file rather than to the network. RFC 2557 handles this with the Content-Location header. Each resource part is stamped with a Content-Location holding the URL that resource had on the original site.
--boundary
Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Location: https://example.com/images/logo.png
When the browser encounters src="images/logo.png" in the root HTML, it resolves that reference against the base URI (the document’s own Content-Location, or the aggregate Content-Location from the outer header) to form an absolute URL, then looks for a part whose Content-Location matches. Relative URLs resolve exactly as they would on the live web, using standard URI base rules, which is why a saved page keeps working even though every link was originally written relative to a server that is no longer involved. RFC 2557 requires that within one multipart/related every part have a distinct Content-Location (or Content-ID), so a lookup is never ambiguous.
cid: references and Content-ID
There is a second way to point at a part: the cid: URL scheme, defined in RFC 2392. Where Content-Location matches on a URL, cid: matches on a part’s Content-ID header. A part might carry Content-ID: <image001@example.com>, and the HTML then refers to it as <img src="cid:image001@example.com"> — the angle brackets are dropped in the URL form. RFC 2557 is strict that a cid: URL must be matched only against Content-ID values, never against a Content-Location that happens to contain a cid: string. This is the same mechanism email uses to embed inline images in HTML mail, which is unsurprising given MHTML and inline HTML email share the RFC 2557 lineage. In practice, browser-saved MHTML leans on Content-Location for most resources and reserves cid: for parts that never had a natural URL.
Transfer encodings: base64 and quoted-printable
Because the container is a text message that historically had to survive 7-bit mail transports, binary resources cannot be stored raw. Each part declares a Content-Transfer-Encoding that says how its bytes were made text-safe. Two encodings dominate MHTML.
| Encoding | Used for | How it works |
|---|---|---|
base64 | Images, fonts, other binary resources | Every 3 bytes become 4 ASCII characters from a 64-symbol alphabet; expands data by about 33%, wrapped to short lines. |
quoted-printable | HTML, CSS, mostly-text resources | Printable ASCII is left as-is; other bytes become = followed by two hex digits, so text stays largely readable in the raw file. |
8bit / 7bit | Plain text where the transport allows it | No transformation; the data is passed through unchanged. |
A decoder reads each part’s Content-Transfer-Encoding, reverses that transform, and recovers the original bytes of the image or stylesheet. This is why a PNG embedded in an MHTML file shows up in the raw text as a long block of wrapped base64 rather than binary noise, and why an embedded stylesheet stays mostly legible under quoted-printable.
A minimal MHTML file, annotated
Putting the pieces together, a small but complete MHTML file with one HTML page and one embedded image looks like this:
From: <Saved by Blink>
Subject: Example page
Date: Mon, 20 Jul 2026 12:00:00 -0000
MIME-Version: 1.0
Content-Type: multipart/related;
type="text/html";
boundary="----=_NextPart_000_0000"
------=_NextPart_000_0000
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Location: https://example.com/index.html
<!DOCTYPE html><html><body>
<h1>Hello=E2=80=99s world</h1>
<img src=3D"images/logo.png">
</body></html>
------=_NextPart_000_0000
Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Location: https://example.com/images/logo.png
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR4nGNgYAAAAAMAASsJTYQA
AAAASUVORK5CYII=
------=_NextPart_000_0000--
Reading top to bottom: the outer headers declare a multipart/related whose root is text/html. The first part is that HTML, quoted-printable encoded (note =E2=80=99 for a curly apostrophe and =3D for the literal = in src=), tagged with the page’s Content-Location. The second part is the logo, base64 encoded, tagged with the image’s original URL. When the browser hits src="images/logo.png", it resolves that against https://example.com/index.html to https://example.com/images/logo.png, finds the matching part, base64-decodes it, and paints the image. The closing ------=_NextPart_000_0000-- ends the message.
Why the boundary must be unique, and how parts stay intact
The whole scheme rests on one guarantee: the boundary delimiter must never appear inside any part’s encoded data, or the parser would split a part in the wrong place. Writers ensure this by generating a long, near-random boundary such as ----=_NextPart_000_0000_01D8ABCD.12345678. Two facts make that safe. First, the transfer encodings keep part data well-behaved: base64 emits only its 64-symbol alphabet plus line breaks, and quoted-printable escapes anything unusual, so neither can accidentally reproduce a hyphen-prefixed boundary line. Second, a delimiter is only recognised at the start of a line as two hyphens plus the exact boundary string, which a random 30-character token will not collide with. RFC 2046 defines this boundary matching, and MHTML inherits it unchanged.
One consequence worth noting: because .mht and .mhtml are byte-for-byte the same MIME structure, converting between them is renaming, not re-encoding. A tool that claims to “convert MHT to MHTML” changes only the filename. Real transformation happens only when you go the other way, unpacking the parts back into a folder of separate files (recovering plain HTML plus its images) or flattening the rendered page to a fixed format like PDF, at which point the MIME container is discarded and the decoded resources are written out on their own.
Who writes and reads MHTML
MHTML was popularised by Microsoft Internet Explorer, whose “Save as > Web Archive, single file (*.mht)” produced these files, and Microsoft Word can both save and open them. Internet Explorer was retired on 15 June 2022, so the modern readers are the Chromium browsers: Microsoft Edge, Google Chrome and Opera all open .mhtml directly, and Chromium can save a page as MHTML through its “Webpage, Single File” option (the From: <Saved by Blink> header in the sample above is Chromium’s signature). Firefox does not read MHTML natively and needs an add-on. Because the parts are just MIME, an .mhtml file is closely related to an .eml email message: both are RFC 822 messages built from MIME parts, and many mail libraries can parse an MHTML file with the same code they use for mail. That shared structure is also why some mail systems serve MHTML with the message/rfc822 media type even though its true content type is multipart/related.
References
- IETF RFC 2557 — MIME Encapsulation of Aggregate Documents, such as HTML (MHTML)
- IETF RFC 2392 — Content-ID and Message-ID Uniform Resource Locators (the
cid:scheme) - Wikipedia — MHTML
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.