Security headers tell the browser to apply protections the server cannot enforce by itself: refusing to sniff content types, restricting where scripts may load from, limiting referrer leakage and forcing HTTPS. They are cheap to add and belong in one central place.
Before you start
You should be comfortable with HTTP responses and middleware. This article covers response headers; it assumes HTTPS is terminated in front of the app.
Step-by-step walkthrough
Step 1: Set headers once, centrally
Add the headers in a single middleware or a reverse-proxy config so every response gets them. Duplicating them in handlers means some route eventually misses one, and setting a header twice can produce conflicting values.
Step 2: Start with a restrictive content security policy
Content-Security-Policy: default-src 'self' blocks scripts and styles from other origins unless explicitly allowed. Start restrictive and add sources as needed; a permissive policy with unsafe-inline provides little protection. For an API that returns no HTML, default-src 'none' is a strong default.
Step 3: Enable HSTS only over HTTPS
Strict-Transport-Security tells the browser to use HTTPS for future requests, and it should be set only when HTTPS works everywhere, or users can be locked out. X-Content-Type-Options: nosniff and a sensible Referrer-Policy are safe everywhere.
Worked scenario
A middleware applies a defensive header set to every response.
function securityHeaders(res) {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Referrer-Policy', 'no-referrer');
res.setHeader('Content-Security-Policy', "default-src 'self'");
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
}Walk through the example
Each header closes a specific gap: nosniff stops the browser from treating a text response as a script, the CSP limits resource origins, the referrer policy reduces leakage to third parties, and HSTS upgrades future requests. Setting them in one function means every route inherits them.
Common mistake
Setting HSTS with a long max-age before HTTPS is fully deployed, which can prevent users from reaching the site over HTTP even during an outage. Another is a CSP so strict it blocks the app’s own assets, so it gets disabled entirely instead of tuned.
Verify the behavior
Check responses with a header scanner or curl -I and confirm each header is present once. Test that the CSP blocks an inline script in a browser. Verify that HSTS is only sent over HTTPS and not in local development.
Interview exercise
Why does X-Content-Type-Options: nosniff matter?
Answer and reasoning
Without it, some browsers guess a response’s type from its content and may execute a file that was uploaded as text but looks like a script. nosniff forces the browser to trust the declared Content-Type, which closes a content-sniffing attack path, especially for user-uploaded files served from the same origin.
Continue learning
Compare transport security in TLS identity and CORS boundaries. Read the MDN HTTP security headers guide and try the Node.js interview questions.