# X-Content-Type-Options is set

- **Pattern:** `ab-002531` (`site-health-check.security-posture.x-content-type-options`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-20
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002531
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002531)

## Why it matters

Browsers with MIME sniffing enabled attempt to guess a file's content type by reading its bytes, ignoring the `Content-Type` header. Attackers exploit this to serve malicious payloads in files uploaded or hosted as innocuous types — a file labeled `text/plain` containing JavaScript can be executed as a script. This crosses directly into CWE-79 (XSS) territory and OWASP A03 (Injection). The `X-Content-Type-Options: nosniff` header is a single-line fix that disables this guessing. It costs nothing to send and eliminates an entire class of content-type confusion attacks on every browser that honors it, which is all major browsers.

## Severity rationale

Low because MIME sniffing attacks require an attacker-controlled upload or hosting vector, making exploitation dependent on other weaknesses being present first.

## Remediation

Add the `X-Content-Type-Options: nosniff` header to all responses. This is a single directive with no configuration options:

```nginx
# nginx
add_header X-Content-Type-Options "nosniff" always;
```

In Next.js `next.config.js`:

```js
{ key: 'X-Content-Type-Options', value: 'nosniff' }
```

Most CDNs (Vercel, Cloudflare) also expose this as a toggle in their security settings dashboard. Confirm with `curl -sI https://yoursite.com | grep -i x-content-type`.

## Detection

- **ID:** `x-content-type-options`
- **Severity:** `low`
- **What to look for:** Count the occurrences of `X-Content-Type-Options` in the response headers. At least 1 response must include this header. Extract the header value and verify it is exactly the string `nosniff` (case-insensitive).
- **Detector snippet (shell-capable tools only):** If `curl` is available, fetch HEAD against `$BASE_URL` and inspect the response headers for `X-Content-Type-Options`. If present with value `nosniff`, pass. If missing, fail. Exit >=2 / command not found — fall back to prose reasoning.

  ```detect-curl
  curl -sS -I $BASE_URL/
  ```

- **Pass criteria:** Exactly 1 `X-Content-Type-Options` header is present in the response and its value is `nosniff`. The value must not contain extra directives or trailing characters beyond `nosniff`.
- **Fail criteria:** Header is missing from the response, or the value is set to something other than `nosniff`.
- **Skip (N/A) when:** The response is a redirect (3xx status) rather than a final document response, since headers on intermediate redirects are not delivered to the browser.
- **Detail on fail:** `"No X-Content-Type-Options header — browsers may MIME-sniff responses"`
- **Remediation:** This header prevents browsers from guessing (MIME-sniffing) the content type, which can lead to XSS attacks. It requires only a single header:

  ```
  X-Content-Type-Options: nosniff
  ```

  In nginx: `add_header X-Content-Type-Options "nosniff" always;`. In Next.js `next.config.js`, add it to the `headers()` array. Most CDNs (Vercel, Cloudflare) also provide this as a toggle in their dashboard.

## External references

- cwe CWE-79
- owasp A03

Taxons: injection-and-input-trust

HTML version: https://auditbuffet.com/patterns/ab-002531
