HTTP security headers let a server tell browsers which security rules should apply to a response. They can restrict where content loads from, prevent MIME type guessing, reduce referrer leakage, and enforce encrypted transport.
They are useful defense in depth, not a replacement for input validation, output encoding, authentication controls, or secure session handling. A strong header policy can limit the impact of some mistakes, but it cannot make an unsafe application secure by itself.
Start with the threat you want to reduce
Do not add headers only because a scanner recommends them. Each header should correspond to a security property you understand.
Common goals include:
- restricting script, style, image, frame, and connection sources;
- forcing future browser connections to use HTTPS;
- preventing content-type sniffing;
- controlling how much referrer information leaves the site;
- restricting browser features available to a page;
- preventing sensitive pages from being embedded by untrusted sites.
This makes header configuration easier to review and avoids policies that look strict but do not match how the application actually behaves.
Use Content Security Policy deliberately
Content Security Policy (CSP) controls which resources a browser may load or execute. It is particularly valuable as an additional barrier against cross-site scripting and unwanted content injection.
A small static site might begin with:
Content-Security-Policy: default-src 'self'; img-src 'self' https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'This policy establishes a same-origin default, disables plugins through object-src, restricts the document base URL, and prevents framing.
Real applications often need more sources. Add them narrowly rather than weakening the entire policy. For example, allowing one analytics endpoint in connect-src is safer than broadly allowing every HTTPS origin.
Avoid using 'unsafe-inline' or 'unsafe-eval' as a routine fix for CSP errors. They weaken important script protections. When inline scripts are unavoidable, nonce- or hash-based policies usually provide tighter control.
Deploy complex CSP changes carefully. Content-Security-Policy-Report-Only can help observe violations before enforcement, but reports may contain sensitive URLs or other data, so handle them as security telemetry.
Enforce HTTPS with HSTS after HTTPS is reliable
HTTP Strict Transport Security tells supporting browsers to use HTTPS for future requests to a host.
A typical header is:
Strict-Transport-Security: max-age=31536000; includeSubDomainsThe browser remembers the policy for the specified duration. This reduces opportunities for downgrade attacks after the browser has learned the rule.
HSTS requires operational care. Do not add includeSubDomains until every relevant subdomain can reliably serve HTTPS. A long max-age can make a configuration mistake persistent for users who have already received the header.
The optional preload directive is not merely another hardening switch. Browser preload programs have additional requirements and can make HTTPS enforcement effective before a first visit, but removal can take time. Treat preloading as a deliberate operational commitment.
Disable MIME type sniffing
Send:
X-Content-Type-Options: nosniffThis instructs browsers not to reinterpret certain responses as a different MIME type. It helps ensure that the declared Content-Type is respected in security-sensitive resource loading.
The header works best when the application also sends correct content types. nosniff should not be used to hide incorrect response metadata.
Limit referrer information
Browsers may send a Referer header when users navigate or load resources. URLs sometimes contain internal paths or other information that should not be exposed unnecessarily.
A practical policy is:
Referrer-Policy: strict-origin-when-cross-originIt allows useful referrer information for same-origin requests while reducing detail sent to other origins and avoiding referrer transmission when moving from HTTPS to HTTP.
Applications with stronger privacy requirements can choose a more restrictive policy such as no-referrer. Select the policy based on what analytics, diagnostics, and external integrations genuinely require.
Control framing with CSP
Untrusted framing can support clickjacking attacks by placing an application inside another page and misleading users about what they are interacting with.
Modern applications can restrict framing with CSP:
Content-Security-Policy: frame-ancestors 'none'Use 'self' instead when legitimate same-origin framing is required. If trusted external origins must embed the application, list them explicitly.
X-Frame-Options remains useful for compatibility with older clients, but frame-ancestors provides the more flexible modern control. If both are sent, keep their intended policies consistent.
Restrict unnecessary browser capabilities
Permissions Policy can limit access to selected browser features. For an application that does not need the camera, microphone, or geolocation, a restrictive example is:
Permissions-Policy: camera=(), microphone=(), geolocation=()Reducing unused capabilities narrows what compromised or unexpectedly embedded content can attempt to use. Do not disable features blindly if legitimate application workflows depend on them.
Apply headers at the correct scope
Not every response needs exactly the same policy.
An HTML document may need a CSP, while an API JSON response usually does not benefit from the same resource-loading directives. Authentication pages may require stricter framing rules than pages intentionally designed for embedding.
Central middleware or an edge proxy can provide secure defaults, but allow reviewed exceptions where application behavior requires them. Scattered per-route configuration is harder to audit and easier to forget.
Also consider error responses. Security headers can disappear when errors are generated by a proxy, framework, or upstream component instead of the normal application path. Test representative success and failure responses.
Avoid contradictory configuration
Headers are only one layer in a chain that may include a CDN, reverse proxy, load balancer, web server, and application framework. More than one layer may add or replace the same header.
Inspect the final response seen by the browser. Duplicate or conflicting policies can produce surprising behavior and make troubleshooting difficult.
Document which layer owns each security header. Prefer one clear source of truth when possible.
Test before and after deployment
Header changes can break applications when policies are stricter than actual resource requirements.
A practical rollout is:
- inventory scripts, styles, images, frames, fonts, and network destinations;
- define the intended policy and its security goal;
- test it in development and staging;
- use report-only CSP where useful;
- inspect browser developer tools for violations;
- deploy gradually when the application has significant traffic;
- verify final production responses at the browser-facing edge.
Automated tests can also assert critical headers. This is especially useful when proxies or deployment templates are frequently changed.
Do not mistake headers for vulnerability fixes
A CSP can make some script injection harder to exploit, but the underlying injection bug should still be fixed. HSTS does not repair weak TLS configuration. nosniff does not validate uploaded files. Framing restrictions do not replace authorization checks.
Treat security headers as controls that reduce exposure when another layer fails.
A practical baseline
For a conventional HTTPS web application, a starting point might include:
Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'This is an example, not a universal configuration. CSP resource directives must match the application’s real dependencies, and HSTS settings must match the organisation’s HTTPS readiness.
Final checklist
Before relying on HTTP security headers:
- define the security purpose of each header;
- keep CSP sources as narrow as practical;
- avoid unnecessary unsafe CSP exceptions;
- enable long-lived HSTS only after HTTPS is dependable;
- send accurate content types with
nosniff; - choose a referrer policy appropriate to privacy needs;
- restrict framing unless embedding is intentional;
- disable browser capabilities the application does not use;
- verify headers on both normal and error responses;
- inspect the final response after every proxy and CDN layer;
- test policy changes for application breakage;
- fix underlying vulnerabilities instead of relying on headers to contain them.
Security headers are most effective when they are small, intentional policies that reinforce secure application design. Configure them according to the application’s actual trust boundaries and maintain them as those boundaries change.