Which requests does `X-Content-Type-Options: nosniff` actually refuse, and why is an image response untouched?
answer
- one header, two different effects
- the block is narrower than expected
- script-like and style destinations only
- images left out for compatibility
- declared markup stays markup
basics
~20 sThe block fires only on a script-like destination whose type is not a JavaScript MIME type, and on a style destination whose type essence is not text/css. Image destinations were deliberately left out, so a mis-typed image still loads.
solid answer
~50 sOne header has two different effects, and only one of them refuses anything. Everywhere, `nosniff` sets the no-sniff flag so the computed MIME type follows the type the response declared. Separately, the fetch layer runs a **blocking** check that is far narrower than the reputation suggests: a **script-like** destination — what a script element, a worker or a service worker creates — whose computed type is not a JavaScript MIME type is refused, and a **style** destination whose type essence is not `text/css` is refused. Nothing else is. **Image destinations were deliberately excluded**, because the web carries far too many mis-labelled images for blocking them to be survivable. So `nosniff` stops a JSON endpoint being pulled in through a script element, and does not stop a mis-typed image loading — and it never rescues a response you yourself declared as markup.
code
http · 5 linesHTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniff
{"parcel":"41-207-018","status":"under review"}go deeper
Recall that the header makes the declared Content-Type authoritative, and that it refuses only a small set of cases rather than everything mis-typed.
Explain the split: type determination applies everywhere, while the refusal applies to script-like and style destinations only.
State the rule by destination, say why images were excluded, and name the limit — the header honours a declaration, it does not correct one.
Judge which hardening headers still earn their place and which are inherited folklore, and set the expectation that a retired header is removed rather than carried forward.
## One header, two effects — keep them apart Almost every wrong answer about `nosniff` comes from collapsing two separate mechanisms into one sentence. **Effect one: type determination.** The header sets the no-sniff flag, so the computed MIME type follows the supplied one — the `Content-Type` the response declared — instead of being inferred from the body's leading bytes. This applies to every response carrying the header. **Effect two: blocking.** The fetch layer asks a separate question — should this response be blocked due to nosniff? — and it asks it about the **request's destination**, not about the response alone. The answer is yes in only two cases. ## The blocking rule, exactly | destination | blocked when | example of a refusal | |---|---|---| | script-like (a script element, a worker, a service worker) | the computed type is not a JavaScript MIME type | a JSON endpoint pulled in through a script element | | style | the type's essence is not `text/css` | a stylesheet served with a generic type | | image | never — deliberately excluded | a mis-labelled image still loads | | document navigation, data fetches, everything else | never blocked by this rule | a mis-typed upload still renders per its declared type | That is the whole rule. Two destinations, one condition each. ## Why images were left out The exclusion is a compatibility decision, not an oversight. An enormous number of servers declare images with a generic type, the wrong image type, or none at all, and browsers have always rescued them by sniffing. A rule that refused every mis-labelled image would have broken a large fraction of the web on the day it shipped, so image destinations were kept out of the blocking check. Early implementations did apply it to images and that was walked back. The consequence is worth stating plainly, because it is the half candidates get backwards: `nosniff` gives you **no** protection against a response being loaded as an image, whatever its declared type says. ## What this means for the appeal portal An appellant uploads an evidence file. The portal stores it and serves it back. The outcomes divide cleanly: - Served as `text/plain` with `nosniff`: the browser will not sniff it into markup. This is effect one, and it is the real win. - Served as a markup type with `nosniff`: it is markup. A supplied markup or XML type is dealt with before the flag is consulted, so the header changes nothing. If you do not want an upload treated as markup, do not declare it as markup. - Pulled in through a script element while declared as data: refused. This is effect two, and it is why an endpoint that returns structured data cannot quietly be repurposed as a script source. - Loaded as an image, whatever it declares: not refused. Effect two does not cover it. ## Checklist answers this question exposes Three claims to check yourself against, because each stays grammatical when stated backwards: 1. `nosniff` blocks mislabelled responses — **only two destinations**, and images are not among them. 2. `nosniff` makes uploads safe — it pins the computed type to the declared one; the declaration is still yours to get right. 3. `nosniff` replaces the browser XSS filter header of the past — it does not. The two do unrelated things, and one of them no longer exists. That last point is the useful piece of history: `X-XSS-Protection` was a non-standard, vendor-specific filter, and the browsers that shipped it **removed** it rather than repairing it. The filter had to guess at what had been injected, and its guesses could themselves be steered into suppressing or exposing parts of a page — a mitigation that becomes an attack surface is not worth keeping. Sending the header today is inert bytes; knowing it as history is useful, treating it as a live defence is a defect. ## The senior version of the answer A senior answer names both effects, states the blocking rule by destination rather than by header, says out loud that images are excluded and why, and ends with the limit: the header decides how a declared type is honoured, not whether the declared type was the right one. That is the difference between reading a hardening list and having turned this on across a service that also serves files somebody else uploaded.
- A hardening checklist still lists the old browser XSS filter header. What does a conforming browser do with `X-XSS-Protection` today?Nothing. It was a non-standard, vendor-specific filter, and the browsers that shipped it removed it rather than repairing it: the filter had to guess at what had been injected, and those guesses could themselves be steered into suppressing or exposing parts of a page. Sending it now is inert; it is worth knowing as history, not as a defence.
- The evidence file is served as a markup type with `nosniff` set. Does the header stop it being treated as markup?No. `nosniff` pins the computed type to the declared one; it does not change the declaration, and a supplied markup or XML type is handled before the flag is consulted. The fix is on the serving side: declare the type the file actually is rather than relying on the header to undo a wrong declaration.
- What does `nosniff` do on a destination that is neither script-like nor style?It still sets the no-sniff flag, so the computed type follows the declared one rather than the bytes. Nothing is refused. If the declared type is missing or unknown the algorithm still sniffs, but with the flag set it will not conclude a scriptable type such as markup.
saying these in an interview costs you the question
- Claims nosniff blocks a mislabelled image from loading.
- Thinks nosniff refuses every response whose declared type is wrong.
- Believes nosniff makes a markup-typed upload safe to serve.
- Treats nosniff as the replacement for the retired browser XSS filter.
- Says a script element can still load a JSON response under nosniff.