missing-x-content-type-options
X-Content-Type-Options not set
Without this small instruction, browsers can occasionally guess that a file you served — say, an uploaded image — is actually a script and execute it. The fix is one line.
What this means in plain English
When your server sends a file to a browser, it labels the file with what kind of content it contains — image, text, PDF, JavaScript, and so on. By default, browsers don’t fully trust that label: if the file doesn’t look like its label, the browser will sometimes re-guess and re-classify it. That’s called MIME sniffing.
Sounds harmless, but it isn’t always. If your site lets people
upload files (avatars, attachments, comments rendered as text) and
serves them back, an attacker can craft a file that looks like a
normal image and parses as runnable code. Without the
X-Content-Type-Options: nosniff instruction, the browser may
decide it looks more like code than an image and execute it as such.
Your site doesn’t currently send the nosniff instruction.
Why it matters to your business
Two practical scenarios where this matters:
- User-uploaded content. Profile pictures, attachments, uploaded SVG logos. If your site serves these from your domain, an attacker who uploads a crafted file can get their code to run in the next visitor’s browser.
- Logs and unusual files. Plain-text files containing certain byte patterns can also be re-classified by older browsers. Less common today but still a real edge case.
The fix has zero downside on a properly-configured site. If your
site needs the browser to guess at file types to function, that’s a
separate bug worth fixing — nosniff simply tells the browser to
trust your labels.
How to fix it
Add a single instruction to every response. Whoever runs your web server can do this in one line:
nginx:
add_header X-Content-Type-Options nosniff always;
Apache:
Header always set X-Content-Type-Options nosniff
Caddy:
header X-Content-Type-Options nosniff
Verify with curl -I https://your-domain.example/ — the header
should appear in the output.
While you’re there, also confirm your file-upload handling does the right thing:
- Serve uploaded files from a separate subdomain if the risk
warrants it (
uploads.your-domain.examplerather than the main domain). Different subdomain = no shared cookies = an attacker can’t reach the rest of your site even if they slip something through. - Set explicit content types and
Content-Disposition: attachmenton user-uploaded files unless the file type is one you specifically want browsers to display inline.