What a browser does when your content type is wrong
Browsers used to second-guess the content type you declared, by looking at the first few bytes of a file. That guessing is how an uploaded image becomes a script, and one header switches it off everywhere.
The short answer
X-Content-Type-Options: nosniff tells the browser to trust your declared Content-Type instead of guessing from the bytes. Sniffing exists because early servers mislabelled files; the cost is that an uploaded file served as text can be executed as script. The one way it breaks a site is a server declaring the wrong type for assets that were working anyway.
What sniffing was for, and what it enabled
For years a large share of the web declared everything as text/plain or nothing at all, because servers were misconfigured and nobody noticed. Browsers compensated by examining the content: a response that began with <html was treated as HTML whatever the header said, and a file starting with the JPEG magic bytes was treated as an image. That made broken sites work, which is why every browser did it.
The security consequence appears as soon as a site accepts uploads. A file that a visitor uploaded is served back by your application with a declared type. If a browser is willing to override that declaration based on the bytes, then an attacker uploads a file whose contents are HTML with a script in it, your application serves it as an image, and the browser sniffs it, decides it is HTML, and runs the script in your origin.
X-Content-Type-Options: nosniff ends the negotiation. The declared type is used, and if it is wrong the file does not render rather than rendering as something else.
# there is one value, and no reason to send anything else
X-Content-Type-Options: nosniff
# nginx, on every response
add_header X-Content-Type-Options "nosniff" always;
# what nosniff enforces, once it is on
declared contains result
---------------------------- ---------------- --------------------
image/png HTML and script not rendered at all
text/plain HTML shown as plain text
application/octet-stream anything downloaded, not run
text/css not CSS stylesheet rejected
text/javascript JavaScript runs, as intendedThe fourth row is the one that breaks sites, and it is worth knowing before you switch this on: nosniff also enforces the type on stylesheets and scripts. A stylesheet served as text/plain works today by being sniffed, and stops working the moment you send this header.
The one way this breaks a working site
Every other header in this group is safe to add blind. This one has a specific failure, and it is worth ten minutes of checking rather than a rollback.
If any of your scripts or stylesheets are served with the wrong content type, they are currently being rescued by sniffing. Turning it off makes the browser refuse them, and a refused stylesheet is an unstyled page while a refused script is a page whose functionality is missing. The symptom looks nothing like a header change.
Where wrong types come from:
- A static file server with an incomplete type map. Anything it does not recognise gets a default, and newer extensions are the ones missing. A
.mjsmodule or a.webmanifestserved astext/plainis common. - Files served from object storage. The type is set at upload time and stored with the object, so a bad value is permanent until somebody reuploads. Build pipelines that copy assets to a bucket frequently set nothing.
- An application route serving a file. A download endpoint or an asset proxy that forgot to set the header, where the default is often
application/octet-streamor empty.
The order to do this in: check the types first, then send the header.
Request every script and stylesheet your pages reference and read the content-type on each. Anything that is not text/css or a JavaScript type is a file that will be rejected. Fix those, then add the header, and the change is invisible.
Doing it the other way round means finding out from a visitor that your site is unstyled, and the connection back to a one-line configuration change made that morning is not obvious to anybody.
A header on every response, not just on pages
This is worth checking beyond your HTML. The attack it prevents concerns files your application serves back, so the responses that matter most are your uploads, your static assets and any download endpoint, none of which is a page.
This one needs the full crawl
This is a header on every response, not just on pages, so the useful finding covers your uploads and static assets as well as your HTML.
The instant search on this site audits a single page, so rather than show you a verdict it cannot support, this guide sends you to the place the check actually runs.
Does this affect AI search?
No, and there is no indirect path worth constructing either. Content type sniffing is a browser behaviour, the header turns it off, and nothing in retrieval or indexing is affected by either state.
The nearest thing to a connection is the failure described two sections up: if adding this header causes your stylesheets or scripts to be rejected, a client-rendered page can end up producing nothing. That is the same self-inflicted rendering failure that a badly introduced policy can cause, and it is a reason to check your content types rather than a reason not to send the header.
Including this check in an audit is not an argument that it helps you rank. It is a fact about the responses your site sends, available from the same request as everything else.
Why this one sits below the other headers
It is the only Refinement in this group, and the reason is the precondition. The attack requires that your site serves files somebody else supplied. A site with no uploads, no user avatars, no attachments and no document library has nothing for an attacker to plant, so the header protects against a scenario that does not exist there.
Sniffing has also been narrowed in browsers over the years, which reduces the exposure further without removing it. The combination of a narrow precondition and a shrinking behaviour is what puts this below the framing header rather than beside it.
The rating flips on one fact: if your site accepts uploads and serves them back, treat this as Important and check it today. That is a large category, it includes any site with a profile picture, and the chip cannot know which kind of site you are. It is also the only check in the catalogue where adding the recommended header can break a working page, which is why the guide spends more space on the order of operations than on the header itself.
Checking every asset type before you switch it on
The list you need is every script and stylesheet your pages reference, with the type each one is served as.
URL=https://example.com/
curl -s "$URL" \
| grep -oE '(src|href)="[^"]*\.(js|mjs|css)[^"]*"' \
| sed 's/.*="//;s/"$//' \
| sort -u \
| while read -r asset; do
case "$asset" in
http*) full="$asset" ;;
*) full="https://example.com$asset" ;;
esac
type=$(curl -s -I "$full" | grep -i '^content-type:' | cut -d' ' -f2- | tr -d '\r')
printf '%-24s %s\n' "${type:-NONE}" "$asset"
done
# and the header itself, on a page and on an asset
curl -s -I "$URL" | grep -i 'x-content-type-options'
curl -s -I https://example.com/uploads/avatar.png | grep -i 'x-content-type-options'Anything in the first column that is not text/css or a JavaScript type is a file that nosniff will reject. A NONE is the worst case, and it is currently working only because a browser is guessing on your behalf.
Questions this check raises
- What does nosniff actually stop?
- It stops the browser overriding your declared type based on the content it sees. The attack it closes is a user-uploaded file served with a benign type that the browser sniffs as HTML or JavaScript and executes in your origin.
- Can nosniff break my site?
- Only if your server was already declaring types incorrectly and sniffing was silently compensating. The usual casualties are stylesheets served as text/plain and scripts served with no type at all. Checking every asset type before switching it on is the whole of the migration.
- Is this header enough on its own?
- No, and it is not meant to be. It removes one specific class of type-confusion, and it sits below CSP and HSTS in what it prevents. It is on this list because it is a single header with almost no downside once your types are correct.