Why HTTP security headers matter
HTTP security headers let a website communicate security rules to browsers. They can restrict where scripts and other resources load from, force HTTPS, reduce MIME-type confusion, limit browser features and prevent a page from being embedded by an untrusted site. These controls are useful layers of defense, but they do not replace secure application design, input validation, output encoding, access control, patching or safe cookie configuration. Content Security Policy, for example, is intended as defense in depth rather than a replacement for preventing injection vulnerabilities. ([w3.org](https://www.w3.org/TR/CSP/?utm_source=openai))
The safest implementation is not to copy a generic header block and assume the job is finished. First inventory how the site works, then introduce policies in stages, test important workflows and monitor changes after deployment. A marketing site, WordPress installation, single-page application, ecommerce store and API will usually need different policies.
HTTP security headers checklist
Start with the headers that match the behavior of your website. A practical baseline often includes the following:
- Content-Security-Policy: controls which origins may provide scripts, styles, images, frames, fonts, connections and other resources.
- Content-Security-Policy-Report-Only: lets you test a proposed CSP without blocking resources.
- Strict-Transport-Security: tells supporting browsers to use HTTPS for future requests to the host.
- Permissions-Policy: limits access to browser capabilities such as camera, microphone, geolocation and fullscreen.
- X-Content-Type-Options: nosniff: tells browsers to respect declared MIME types and disables certain forms of content sniffing.
- Content-Security-Policy frame-ancestors or X-Frame-Options: limits whether other pages can embed your content.
- Referrer-Policy: controls how much referrer information browsers send to other sites.
Not every header belongs on every response. For example, CSP is most relevant to documents that load or interpret content, while an API response may need a different set of controls. Apply headers at the correct layer and verify that redirects, error pages, static files, cached responses and application-generated responses behave as expected. ([cheatsheetseries.owasp.org](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html?utm_source=openai))
Content Security Policy: the most important and most difficult header
Content Security Policy, or CSP, defines which resources a browser may load or execute for a document. Directives can control scripts, styles, images, fonts, connections, frames, objects, forms, workers and other resource types. CSP can reduce the impact of cross-site scripting and content-injection attacks by restricting what an injected element can do, but it should not be treated as a complete XSS prevention strategy. ([w3.org](https://www.w3.org/TR/CSP/?utm_source=openai))
Start with a resource inventory
Before writing a policy, inspect the site in a browser and record every external dependency. Include analytics, tag managers, advertising systems, payment providers, video players, maps, chat widgets, cookie-consent tools, fonts, CDNs, API endpoints, web workers, iframes and form destinations. Also identify inline scripts, inline styles, dynamically generated scripts and code that uses eval() or similar dynamic execution.
Use browser developer tools to review the Network and Console panels. Test the homepage, contact forms, login, checkout, search, account areas, embedded media and administrative workflows. A policy that works on the homepage can still break a checkout button or a third-party login flow.
Use report-only mode first
The Content-Security-Policy-Report-Only response header allows a team to observe violations without enforcing the policy. This makes it suitable for staged deployment. A report-only policy does not block the resource, but browsers can display violations in developer tools and send reports when reporting is configured. ([w3.org](https://www.w3.org/TR/CSP/?utm_source=openai))
A deliberately conservative starting point might look like this:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'This is an example for investigation, not a universal production policy. External services will need to be added explicitly, and some sites will require nonces or hashes for legitimate inline code. Avoid adding * everywhere merely to silence reports; that can make the policy much less useful.
Understand common CSP directives
default-srcprovides a fallback for many resource types.script-srccontrols JavaScript sources and is central to XSS protection.style-srccontrols stylesheets and inline style behavior.img-srccontrols image sources.font-srccontrols font sources.connect-srccontrols connections made by fetch, XMLHttpRequest, WebSocket and related APIs.frame-srccontrols documents embedded by the page.frame-ancestorscontrols which sites may embed the protected document.object-src 'none'disables legacy plugin content such asobjectandembed.base-urilimits the URLs that can be used in a document'sbaseelement.form-actionlimits where forms may submit.upgrade-insecure-requestsasks the browser to treat insecure resource URLs as HTTPS where possible.
The CSP header is the preferred delivery mechanism because it is sent before the document is processed and supports the full policy feature set. A meta element can be useful when server headers cannot be changed, but it is not equivalent in capability and must be placed early in the document. ([w3.org](https://www.w3.org/TR/CSP/?utm_source=openai))
Move from report-only to enforcement carefully
After reviewing reports, remove unexpected third-party domains, correct application bugs and document every required origin. Then deploy a small enforced policy to a representative environment or a limited route. Keep report-only monitoring active with a stronger candidate policy if your reporting design supports it.
For inline scripts, prefer external files or use a server-generated nonce. A nonce must be unpredictable, generated for the response and inserted into both the CSP and the permitted script element. Avoid broadly allowing 'unsafe-inline' for scripts unless you understand the security trade-off. Likewise, do not add 'unsafe-eval' simply because a library has not been updated; identify whether the dependency can be replaced or reconfigured.
HSTS setup: force HTTPS after TLS is reliable
HTTP Strict Transport Security, or HSTS, tells a browser that future requests to a host must use HTTPS. It also prevents users from bypassing certain certificate errors for an HSTS-protected host. HSTS is learned through an HTTPS response; an attacker cannot safely activate it for a victim through an ordinary HTTP response. ([cheatsheetseries.owasp.org](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html?utm_source=openai))
Begin with a short duration while confirming that every relevant hostname works over HTTPS:
Strict-Transport-Security: max-age=86400Once certificates, redirects, subdomains and operational procedures are reliable, many sites increase the duration and may add includeSubDomains:
Strict-Transport-Security: max-age=63072000; includeSubDomainsUse includeSubDomains only when all current and future subdomains are prepared for HTTPS. A forgotten legacy host, development system or third-party service under the same parent domain can become inaccessible after the policy is cached. Treat preload as a separate, high-commitment decision. It signals intent to be included in browser-maintained preload lists, but submitting a domain can make recovery from configuration mistakes more difficult. ([cheatsheetseries.owasp.org](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html?utm_source=openai))
HSTS is not a replacement for a correct HTTP-to-HTTPS redirect, valid certificates, secure cookies or TLS configuration. Keep the redirect for users who first type an HTTP URL, and verify that no mixed-content resources remain.
Permissions Policy: reduce unnecessary browser capabilities
Permissions-Policy controls whether a document and its embedded frames may use selected browser features. Depending on browser support and the feature involved, this can include capabilities such as geolocation, camera, microphone, fullscreen and other powerful APIs. The header uses feature-specific allowlists, and an embedded cross-origin frame may also need a matching allow attribute. ([developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy?authuser=0&utm_source=openai))
If a site does not need a feature, disable it explicitly. For example:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()If a trusted embedded service needs access, name the origin narrowly instead of using a wildcard:
Permissions-Policy: geolocation=(self "https://maps.example")Review this header whenever iframe providers change. A policy can appear correct while a frame still fails because the parent policy, the iframe's allow attribute and the browser's permission prompt all need to permit the feature. Permissions Policy also has uneven browser support for some directives, so test the specific feature and browsers relevant to your audience. ([developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy?authuser=0&utm_source=openai))
Supporting headers that belong in the baseline
X-Content-Type-Options
Send the following header on responses where it is appropriate:
X-Content-Type-Options: nosniffWith nosniff, browsers respect the declared MIME type instead of guessing in contexts where that could cause a script or stylesheet to be interpreted incorrectly. It works best when every resource also has an accurate Content-Type. Incorrect MIME types can cause legitimate JavaScript or CSS to stop loading after this header is enabled, which is a configuration problem to fix rather than a reason to remove the protection. ([developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options?isappinstalled=0&utm_source=openai))
Clickjacking protection
If a page should never be embedded, use:
Content-Security-Policy: frame-ancestors 'none'If same-origin framing is required, use frame-ancestors 'self'. For compatibility with older clients, many sites also send X-Frame-Options: DENY or X-Frame-Options: SAMEORIGIN. CSP's frame-ancestors directive is the more flexible modern control, while X-Frame-Options remains a useful compatibility header. ([cheatsheetseries.owasp.org](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html?utm_source=openai))
Referrer-Policy
A reasonable modern baseline for many websites is:
Referrer-Policy: strict-origin-when-cross-originThis generally preserves useful same-origin detail while limiting cross-origin referrer information to the origin. Choose a stricter policy if URLs may contain sensitive information, and ensure sensitive data is never placed in query strings merely because a referrer policy exists.
Nginx configuration example
Nginx can add arbitrary response headers with the add_header directive. Without always, headers are only added for selected response codes; using always helps cover error responses as well. Be careful with nested locations because Nginx header inheritance can change when another add_header directive is defined at a lower configuration level. ([nginx.org](https://nginx.org/en/docs/http/ngx_http_headers_module.html?utm_source=openai))
server {
listen 443 ssl http2;
server_name example.com;
add_header Strict-Transport-Security "max-age=86400" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
# Application configuration follows
}Test the configuration before reloading Nginx, then inspect several response types:
sudo nginx -t
sudo systemctl reload nginx
curl -I https://example.com/
curl -I https://example.com/missing-page
curl -I https://example.com/static/app.jsApache and WordPress considerations
Apache can set headers with mod_headers, either in the virtual host configuration or, where permitted, in .htaccess. A typical starting point is:
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=86400"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Content-Security-Policy-Report-Only "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
</IfModule>For WordPress, identify which layer owns the response: Apache, Nginx, a CDN, a hosting control panel, a security plugin or the application itself. Do not configure the same header independently in several layers unless you understand how duplicate policies interact. Multiple CSP policies are evaluated together, so an accidentally duplicated policy can make the effective result more restrictive than expected. ([w3.org](https://www.w3.org/TR/CSP/?utm_source=openai))
WordPress sites commonly load plugins, themes, analytics, fonts, form providers and embedded media from different origins. Roll out CSP in report-only mode, test the public site and administrator workflows, and review plugin updates because a previously stable policy can break when a plugin changes its scripts or asset URLs.
Verification and CSP troubleshooting workflow
- Inspect the actual response: use browser developer tools,
curl -Iand a header scanner. Check the final HTTPS response after redirects, not only the HTTP response. - Test more than the homepage: check forms, login, checkout, account pages, search, embedded content, PDFs, images, APIs and error pages.
- Read the exact browser violation: identify the blocked directive, requested URL, resource type and document that generated the request.
- Classify the cause: the source may be legitimate, obsolete, unexpected or malicious. Never approve an origin solely because it appears in a violation report.
- Fix the narrowest problem: add a specific trusted origin or change the application to use an external file, nonce or hash. Avoid broad wildcards and unsafe keywords.
- Check caches and CDNs: purge cached HTML and verify that the edge, origin and application are not sending conflicting header values.
- Keep rollback available: store the previous configuration, know how to disable enforcement and monitor key conversion and login workflows after deployment.
Common CSP errors include allowing a script origin but forgetting its API endpoint in connect-src, permitting a frame in frame-src while blocking the parent relationship with frame-ancestors, loading fonts from a CDN without updating font-src, and enabling nosniff while serving JavaScript with the wrong MIME type.
Final website security headers checklist
- All public pages and sensitive workflows are available over HTTPS.
- HTTP redirects to HTTPS correctly.
- HSTS is enabled only after certificates and subdomains are verified.
- CSP was tested in report-only mode before enforcement.
- CSP uses narrow origins and avoids unnecessary wildcards.
- Inline scripts are migrated, hashed or protected with nonces where practical.
- Third-party scripts and iframe providers are inventoried and reviewed.
X-Content-Type-Options: nosniffis paired with correct MIME types.- Framing is explicitly controlled with
frame-ancestorsor X-Frame-Options. - Unused browser capabilities are disabled with Permissions Policy.
- Headers are verified on redirects, error pages, cached pages and static assets.
- A documented rollback procedure exists for policy changes.
HTTP security headers are most effective when treated as maintained configuration rather than a one-time score improvement. Recheck them after hosting migrations, CDN changes, plugin updates, new analytics integrations and redesigns. For broader hardening, connect this guide with the small-business cybersecurity guide, the Core Web Vitals troubleshooting guide and the Linux server hardening checklist.
