Security response headers ask a supporting browser to enforce specific rules. Their value comes from the behavior those rules change, not from the length of a header checklist. A static site still has document loading, scripts or styles, embedding, navigation, and transport boundaries to consider. Select policies that match the actual page and delivery path, then verify both intended restrictions and legitimate functionality.

Content Security Policy can restrict resource loading and script execution, while frame-ancestors specifies which ancestors may embed the protected document. Referrer-Policy controls how much source-URL information is sent with applicable requests. Permissions-Policy restricts selected browser features. These are related controls, but none establishes an authenticated business permission simply by being present. A policy appropriate for one response category may be irrelevant or disruptive on another.

Understand the boundary each header owns

HSTS tells the browser to use HTTPS for its applicable host policy. Browsers learn that policy from a valid HTTPS response; an HTTP response cannot establish it. Including subdomains affects more than the current application and requires those hosts to support the intended transport. HSTS does not issue certificates or repair an expired certificate. Transport configuration must already be sound before a lasting browser policy is relied upon.

Cross-Origin-Opener-Policy affects browsing-context and opener isolation, whereas Cross-Origin-Resource-Policy restricts certain cross-origin resource loads. Treating both as a generic switch that isolates every window confuses their contracts. Likewise, CORS response exposure is a separate mechanism. Verify integrations that depend on popups, embedded resources, or cross-origin access when changing the relevant policy, rather than copying every available header into one undifferentiated rule.

When it breaks

X-Content-Type-Options: nosniff makes declared content types matter to browser processing. For script and stylesheet destinations, the browser rejects responses whose MIME type is not appropriate for that destination. A deployment that serves JavaScript as plain text can therefore stop working when nosniff is enabled. The repair is to serve the intended bytes with an appropriate type and status, not simply remove the restriction until the symptom disappears.

Another common failure is a route that returns an HTML error page where a script or stylesheet was expected. A correct policy may expose that existing delivery mistake. Inspect the actual response after redirects, caches, and routing, including Content-Type and status. A header-presence test cannot tell whether the response is the right asset, and a successful homepage request cannot prove that error responses and static resources follow their intended contracts.

Check it quickly

Save this as nosniff.py and run python3 nosniff.py --serve. Open the printed URL with ?mode=correct, then try ?mode=strict and ?mode=loose. Python 3.14.4 and Chrome 152 were used. The temporary loopback server sends identical classic-script bytes in all three cases, varying the MIME type and nosniff header. The page reports whether the script executed and whether its load failed. Control-C closes the server.

import json
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Event, Thread
from urllib.parse import parse_qs, urlsplit
from urllib.request import urlopen


class Handler(BaseHTTPRequestHandler):
    def log_message(self, *args):
        pass

    def do_GET(self):
        url = urlsplit(self.path)
        mode = parse_qs(url.query).get("mode", ["correct"])[0]
        assert mode in {"correct", "strict", "loose"}
        headers = {"Cache-Control": "no-store"}
        if url.path == "/payload.js":
            body = b"window.payloadExecuted = true;"
            headers["Content-Type"] = "application/javascript" if mode == "correct" else "text/plain"
            if mode != "loose":
                headers["X-Content-Type-Options"] = "nosniff"
        else:
            body = ("""<!doctype html><meta charset="utf-8"><title>MIME policy lab</title>
<h1>Script MIME policy</h1><pre id="result">Running...</pre><script>
const mode=""" + json.dumps(mode) + """;
const script=document.createElement('script');
script.src='/payload.js?mode='+mode;
function done(failed) {
  window.labResult={mode,executed:window.payloadExecuted===true,loadFailed:failed};
  document.getElementById('result').textContent=JSON.stringify(window.labResult,null,2);
}
script.onload=()=>done(false);script.onerror=()=>done(true);document.head.append(script);
</script>""").encode()
            headers["Content-Type"] = "text/html; charset=utf-8"
        self.send_response(200)
        self.send_header("Content-Length", str(len(body)))
        for key, value in headers.items():
            self.send_header(key, value)
        self.end_headers()
        self.wfile.write(body)


server = HTTPServer(("127.0.0.1", 0), Handler)
thread = Thread(target=server.serve_forever, daemon=True)
thread.start()
origin = f"http://127.0.0.1:{server.server_port}"
try:
    for mode in ("correct", "strict", "loose"):
        with urlopen(origin + "/payload.js?mode=" + mode, timeout=5) as response:
            assert response.read() == b"window.payloadExecuted = true;"
            assert response.headers["Content-Type"] == ("application/javascript" if mode == "correct" else "text/plain")
            assert response.headers.get("X-Content-Type-Options") == (None if mode == "loose" else "nosniff")
    print(json.dumps({"app_url": origin, "wire_mime_checks": "PASS"}), flush=True)
    if "--serve" in sys.argv:
        Event().wait()
except KeyboardInterrupt:
    pass
finally:
    server.shutdown()
    thread.join(timeout=5)
    server.server_close()

In correct mode, the response uses a JavaScript MIME type with nosniff, and the script executes. Strict mode labels those same bytes as text/plain with nosniff; execution is false and the load-error flag is true. Loose mode removes nosniff from that plain-text response, and this tested browser executes the classic script. The wire probe separately confirms that the bytes are identical and that only the relevant response fields differ.

This browser comparison verifies one concrete MIME boundary. It does not test HSTS persistence, every resource type, opener isolation, or other browsers, and it does not imply that removing nosniff guarantees execution everywhere. For the deployed site, retain focused negative cases alongside functional checks of real pages and assets. Confirm the headers delivered by the final serving layer, and connect each expected value to the browser behavior it is intended to preserve or prohibit.

Further reading

Sources