MIME type
A MIME type — formally a media type — is the label in a response's
Content-Type header telling the client what the body contains. The
browser reads it to decide whether to render the response, play it, or
save it to disk.
Content-Type: text/html; charset=utf-8 → renders
Content-Type: application/octet-stream → downloads
Content-Type: text/plain → shows the markup as text
Over HTTP the header is authoritative and the file extension is
irrelevant. report.html served as application/octet-stream is a
download, every time.
The two headers that decide
| Header | Says | Values that matter |
|---|---|---|
Content-Type |
What the bytes are | text/html, application/octet-stream, text/plain |
Content-Disposition |
What to do with them | inline, attachment; filename="…" |
Content-Disposition: attachment wins over a correct Content-Type. If
a page downloads despite text/html, that header is why.
Where reports get this wrong
- S3 and compatible object stores. An upload with no declared type
is stored as
application/octet-streamorbinary/octet-stream, so the report downloads. Fixed at upload time with--content-type text/html, not afterwards on the link (the full fix). - CI artifact endpoints. Many serve every artifact as a download by policy, regardless of type — which is why a GitHub Actions "report link" is a zip (why).
- Chat and ticket attachments. HTML is served as a download deliberately, because rendering user-supplied HTML in the app's own origin would be an XSS vector (why Slack won't preview).
That last one is not a misconfiguration. Serving arbitrary HTML inline from an origin that holds sessions is genuinely dangerous — which is why services that do render report HTML do it inside a sandboxed iframe on a separate origin.
Charset matters too
text/html without charset=utf-8 leaves the browser guessing, and the
guess is wrong for any report containing non-ASCII test names, author
names or currency symbols. The <meta charset> in the document helps,
but the header wins where both are present.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history. Reports are served with the headers that make them render.