X-Content-Type-Options: nosniff
Stops the browser guessing a response's type from its contents, so an uploaded text file cannot be run as a script.
Updated 3 November 2026
Check your site's nosniff header
We send one normal request (following redirects) and grade the X-Content-Type-Options header on the final response.
Recommended value
X-Content-Type-Options: nosniff
| Value / directive | What it does |
|---|---|
| nosniff | The only valid value. Scripts and stylesheets are blocked unless their Content-Type is a JavaScript or CSS MIME type. |
What MIME sniffing is
Browsers used to look at the first bytes of a response and decide for themselves what it was, overriding the Content-Type header. That helped badly configured servers, but it also meant a user-uploaded avatar.png that actually contained HTML or JavaScript could be executed when loaded in the right context.
With X-Content-Type-Options: nosniff, the browser trusts your Content-Type. A <script src> pointing at a response labelled text/plain or image/png is refused, and so is a stylesheet without text/css.
What can break
Turning on nosniff exposes wrong Content-Type headers. If a JavaScript file is served as text/plain or application/octet-stream, it stops loading. Check your browser console after enabling it and fix the MIME types at the server (nginx mime.types, or the Content-Type your app sets on generated JS/CSS). Laravel's Vite build output is served by the web server, so the defaults are normally correct.
Pair it with safe uploads
nosniff is one layer. For user uploads also store files outside the web root or on a separate domain, validate MIME type and extension on upload, and serve them with Content-Disposition: attachment where possible.
How to set X-Content-Type-Options
nginx
# inside your server { } block
add_header X-Content-Type-Options "nosniff" always;
Apache
# .htaccess or virtual host (needs mod_headers)
Header always set X-Content-Type-Options "nosniff"
Laravel middleware
// app/Http/Middleware/SecurityHeaders.php
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('X-Content-Type-Options', 'nosniff');
return $response;
}
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\App\Http\Middleware\SecurityHeaders::class);
})
Express (Node.js)
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
next();
});
// or: app.use(require('helmet')()) for a sensible default set
Cloudflare
Rules → Transform Rules → Modify Response Header → Create rule.
Match: all incoming requests (or your hostname).
Then: Set static → Header name: X-Content-Type-Options → Value: nosniff
In nginx, an add_header inside a location block discards every add_header inherited from the server block. Repeat your security headers there, or use an include file. The always flag also sends the header on error responses.
Common mistakes
-
Values other than
nosniff(for exampleno-sniff). They are ignored. - Only setting it on HTML pages. It matters most on uploaded files and API responses, so set it globally.
- Enabling it without checking that JS and CSS are served with the right Content-Type.
FAQ
What does X-Content-Type-Options: nosniff do?
It tells the browser to use the declared Content-Type instead of guessing, and to refuse scripts and stylesheets that are served with the wrong MIME type.
Is X-Content-Type-Options still needed?
Yes. It is cheap, supported by all current browsers, and blocks a class of upload and content-confusion attacks.
Related guides
Headers are one of 30+ things attackers check
The free StackShield scan also checks for exposed .env files, debug mode, leaked tooling, weak TLS and more, from the outside, in about a minute.
Run a free scan