First understand the delivery path
This guide is for operators and developers combining several protection mechanisms. It complements the individual articles and implementation guides. There is no fixed number of headers that every website must set on every response. An HTML page, a stylesheet, a JSON response and a redirect need different consideration.
First establish which hosts and routes are publicly accessible, where TLS terminates and who generates responses. Include static pages, server-rendered pages, authenticated areas, media, downloads and error responses. Identify required embeds, external resources, popups, login providers and payment journeys. A decision such as “no external frames” must fit those functions.
OWASP lists Security Misconfiguration as A02 in its Top 10:2025. Missing or unsuitable security headers are one possible finding within that category, alongside many other configuration issues. The category's rank is not a measured priority for a particular header on your website. Document the protection goal and test; a header list establishes neither a concrete exploit nor complete compliance. OWASP A02:2025.
Four possible layers, with responsibility for each rule
Schematic explanation · not a measurement
Where the response originates
Request path
- Browser
Requests a specific URL.
- CDN / edge
May include a WAF and cache. A cache hit can end the onward path.
- Web server / reverse proxy
Routes the request to a file or application; can generate an error page itself.
- Application
Generates dynamic content and associated headers if the request reaches it.
Response path without an earlier response
- Application → web server / proxy
Headers and content are returned together.
- Web server / proxy → CDN / edge
Rules may add or replace headers.
- CDN / edge → browser
Cache and response rules determine the version delivered.
- Browser
Check headers and content together. Only here is the actually received response available.
Alternative path: cache hit
The CDN/edge serves a stored response.
Browser
The application was not called for this retrieval. Stored headers and stored HTML must still be checked together.
Alternative path: error page generated here. A CDN/WAF or web server/proxy can respond itself. Its error response then bypasses the downstream application; responsibility for its headers must be assigned separately.
A possible delivery path; one service may fulfil several roles. A cache hit or independently generated error page bypasses parts of the normal path.
The following layers describe functions. One service may combine several of them; not every installation consists of four separate servers.
On narrow screens, scroll the table sideways.
| Layer | Suitable tasks | Decisive limitation |
|---|---|---|
| Application and middleware | Route- and user-dependent responses, matching CSP nonces, caching decisions for personal content | May not run when an intermediary serves directly from cache or generates its own error page |
| Web server | Rules for hosts, static files and assigned routes | Inheritance, status codes, internal redirects and modules determine the actual response |
| CDN and edge | Rules close to public delivery, static headers and the edge's own responses | Caching behaviour, function responses and rule order depend on the platform |
| Reverse proxy and WAF | Shared delivery, access control and potentially their own blocking or error pages | A WAF feature is not automatically a freely configurable header filter; test its own responses separately |
Assign one responsible layer for each header and response family. For example, the application may generate a dynamic CSP while the TLS endpoint sets HSTS. This is a deliberate division of responsibility. Maintaining the same CSP independently in several places, however, creates conflicts that are difficult to detect.
A simple responsibility record names the rule, purpose, affected hosts/routes, generating layer, downstream changes, responsible person and acceptance criterion. What matters is a traceable decision about the final response, rather than the number of configuration files.
On narrow screens, scroll the table sideways.
| Example | Suitable integration point | Establish first |
|---|---|---|
| Next.js | Dynamic CSP through the documented proxy/rendering path | Since Next.js 16, the former middleware file convention is named proxy; nonces require dynamic output and consistent propagation |
| Express, Laravel or a comparable application | Middleware or response generation | Which static, error and proxy responses actually pass through the middleware |
| nginx, Apache, Caddy or IIS | Server/host/route configuration | Active modules, inheritance, status codes and upstream systems; .htaccess works only with suitable Apache operation and permitted overrides |
| Astro | Distinguish static routes from routes rendered on request | Middleware may run at build time during prerendering; a later HTTP header must be generated in the actual hosting path |
| WordPress | Responsible application component or serving web server | Do not indiscriminately combine functions.php, plugins and server rules for the same policy; include page and CDN caches in testing |
| Cloudflare | Response Header Transform Rules or a required Worker function | Setting versus appending, match conditions, caching decisions and product limitations |
| Netlify | _headers/configuration for applicable static files; headers in function responses | The documentation states that static header rules do not automatically apply to proxy, Function and Edge Function responses |
| Shopify | Treat the platform storefront and your own Hydrogen application separately | An HTML template is not a freely available response-header filter; your own storefront does not automatically control checkout and other platform responses |
Sources for changing platform details: Next.js CSP, Next.js Proxy, Astro middleware, Netlify header limitations.
For Hydrogen, Shopify documents CSP generation with a nonce and propagation into the rendered HTML. A meta CSP in a theme, in contrast, has the limitations of meta delivery, including those affecting frame-ancestors and Report-Only. Cloudflare now documents an O2O path in front of Shopify with specific prerequisites. This does not promise that every intermediary header rule is supported or every shop journey will work unchanged. Before such an architectural change, check the specific provider conditions and certificate renewal; do not adopt a blanket proxy recommendation. Hydrogen CSP, Cloudflare for Shopify, CSP and meta delivery.
Multiple values do not simply mean “last header wins”
HTTP can transmit several field lines with the same name. How they form an effective rule depends on the field. Proxies and libraries may also combine those lines; a convenient object view therefore does not always reflect the original format. RFC 9110 §§5.2–5.3.
On narrow screens, scroll the table sideways.
| Mechanism | Meaning of multiple values |
|---|---|
| Enforced CSP | Multiple policies apply together. An additional policy cannot relax the first; the resource must satisfy every applicable policy |
| CSP Report-Only | Reports according to its rules, but does not itself block or override an existing enforced policy |
| HSTS | Under RFC 6797, only the first of several STS fields is processed; transport and syntax must also be correct |
| Referrer-Policy | In a policy list, the last recognised valid policy applies; this is not a general rule for other headers |
| Structured fields | The specific grammar determines behaviour. A dictionary, a list and a single structured item are not interchangeable |
An example of an unintended combination:
Content-Security-Policy: script-src 'self'
Content-Security-Policy: script-src 'none'The second field provides no exception for the site's own scripts. It adds a restriction that blocks them. A change should therefore replace an existing policy deliberately or add to it intentionally, rather than rely on assumed precedence. CSP3: multiple policies, HSTS §8.1, Referrer-Policy.
In nginx, the standard model inherits parent add_header directives only if the current level has none of its own. One additional add_header in a location can therefore remove the inherited group. Since 1.29.3, add_header_inherit can change the model; merge appends inherited values and may create duplicates deliberately or unintentionally. always extends status-code coverage but fixes neither incorrectly chosen routes nor headers from a layer that is bypassed entirely. nginx headers module.
In Apache, onsuccess and always are different internal header tables. always is not a general superset of every existing value. CGI/FastCGI and error responses in particular can otherwise produce duplicate fields. Check which table supplies the original value and which table the rule changes. Apache mod_headers.
Headers, HTML and caching must work together
A CSP nonce authorises selected scripts or styles through an unpredictable value. The same value must appear in the header and on the intended element. It must be generated afresh for every transmitted policy; a fixed build-time value or a permanently reused cached nonce is not a suitable substitute. Matching CSP hashes may be an alternative strategy for static content. CSP3: nonce requirements, CSP fundamentals.
A typical failure occurs when the edge generates only a new header but serves old cached HTML with a different nonce. The browser then blocks the intended scripts. If headers and HTML are reused together from a cache, the page may work, but the required fresh nonce is still not established. “Does the application work?” and “Does nonce generation meet the protection design?” need separate answers.
Even a visible Cache-Control: no-store does not prove that an intermediary stored nothing beforehand. Cloudflare makes its cache decision before Response Header Transform Rules; changing that header afterwards does not change the Cloudflare caching decision already made. The appropriate Cache Rules or origin directives govern that decision. Cloudflare Response Header Transform Rules.
Define whether headers and content are generated, stored, invalidated and delivered together. Personal content must be handled correctly at the decisive cache boundary. HTTP caches, service-worker storage and the back/forward cache are different systems. For confidential areas and logout, see Cache-Control and Clear-Site-Data.
Introduce changes gradually, based on actual risk
The following three phases are a working sequence, not a fixed risk ranking. A personal area exposed through shared caching needs immediate attention and must not wait for a later “caching phase”.
- Establish the baseline and address immediate faults. Record HTTPS and certificates, response types, MIME assignments, personal content and existing headers. Determine required frames and referrer transmission. Select appropriate rules for nosniff, frame embedding, Referrer-Policy and HSTS, each with its own scope. Do not present an existing browser default as an explicitly configured operator policy.
- Test application policies. Develop CSP from the sources and functions actually needed; Report-Only can reveal violations. Permissions Policy restricts supported browser features but does not grant user permission. SRI needs stable resources, suitable metadata and an update process. Define how to handle exceptions and dependencies.
- Add specific requirements and observation. Introduce COOP/COEP/CORP only after establishing isolation and embedding needs. Test popup logins, payments and external resources. Operate reporting with a data protection plan, a receiver and evidence of actual reports. Assess additional headers by maturity and target browsers; do not enable them simply because a scanner point is missing.
DNSSEC/CAA, certificate operations and a reachable security contact belong to overall operations, but are not additional HTML headers. DNS security, TLS and security.txt explain their prerequisites.
HSTS needs its own rollback plan. Increase the lifetime only after checking the affected HTTPS hosts; includeSubDomains includes deeper and internal subdomains. Removing the configuration alone does not end a period already stored by a browser. max-age=0 can remove the stored rule after a successful new HTTPS contact; parent and preloaded rules need separate consideration. The preload page consulted recommends HSTS but generally advises against preloading and calls for a deliberate decision. Removal from distributed browser lists takes time. preload therefore does not belong in a general starting snippet. RFC 6797, HSTS Preload: deployment and removal.
Small, assigned rules rather than one universal configuration
A consolidated configuration is useful when it groups rules managed together. It should neither overwrite dynamic application rules nor treat every response type identically. The following nginx snippet shows only two static fields, not a complete security configuration:
# Include in the deliberately chosen server/location context.
# Check inheritance and existing fields first.
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;This assumes correct MIME types and a deliberate choice of this Referrer-Policy for the affected responses. Check the nginx version in use, the entire effective configuration tree and the fields actually delivered. The snippet was checked against the directive documentation but was not executed here as an nginx installation. Server testing and controlled loading of the configuration remain the operator's responsibility.
The corresponding principle in Cloudflare would be an appropriate response-header rule using Set for the selected fields, with limited match conditions. Add can append more fields of the same name. Check the rules and their execution point rather than layering a second independent policy over the origin response. The caching limitations described above apply to Cache-Control.
These two fields do not replace a complete application CSP, blanket includeSubDomains; preload, COEP: credentialless, a global Clear-Site-Data instruction or a fictitious report receiver. Each needs its own justified decision. The individual articles explain conditions and concrete examples.
Acceptance testing on the public response path and in browsers
A configuration check establishes whether a service can load its rules. An HTTP retrieval shows which response was delivered. A browser run tests whether the expected effect follows and the application works. These forms of evidence complement each other.
For an initial HTTP view of your own authorised test domain, use a GET request:
curl -sS -D - -o /dev/null 'https://www.example.com/'To see a bounded redirect chain with individual header blocks:
curl -sS -L --max-redirs 5 -D - -o /dev/null 'https://www.example.com/'Replace the example domain and investigate unexpected redirect destinations before further testing. curl -I uses HEAD. HEAD should return largely the same metadata, but can differ, particularly with dynamic generation, and does not test executed page content. The actual GET response and browser journey therefore matter. Bypassing TLS errors by disabling certificate verification is not successful HTTPS acceptance testing. RFC 9110 §9.3.2.
On narrow screens, scroll the table sideways.
| Test case | Expected evidence |
|---|---|
| Public HTML page and relevant subroute | Appropriate effective policies, functioning navigation, resources and forms |
| Authenticated area with a synthetic test account | Correct authorisation and caching; no personal responses entering shared caches |
| Redirect and different host | Target, HTTPS and appropriate host-specific rules at each step; a policy at the start does not automatically apply to the destination document |
| 404, 500 and CDN/WAF error page | Actual response producer and rules appropriate to that response type |
| Scripts, styles, images, fonts, uploads | MIME type, integrity and embedding conditions appropriate to the loading context |
| Cache hit and revalidation | Expected header/content combination, without an old policy or mismatched nonce |
| Login, popup, payment, embedded application | Successful allowed functionality and deliberately disallowed countercases |
| Logout and revisiting/back navigation | Server session ended, intended effect on local data, no blanket conflation with HTTP caching |
| Reporting | The browser generates the intended report and its configured receiver accepts it; a manually sent test POST alone is insufficient |
Test the browsers and devices actually needed. Chromium does not automatically cover Firefox or Safari, and Playwright WebKit is not evidence from a real Safari device. Some rules change stored browser state; a fresh profile alone therefore does not test every existing user situation. After a successful limited rollout, expand gradually and observe complaints, failures and reports within the intended operational arrangements. An absence of reports does not prove an absence of errors.
In the local synthetic origin/proxy check documented for the German source, matching header/HTML nonces worked. A new header over old HTML blocked the script, as did an additional stricter CSP. A standalone Report-Only policy did not block it. Reused cached nonces allowed the script to run but did not meet the fresh-nonce design. These countercases establish Chromium behaviour in the model, not acceptance of a real CDN or nginx server.
Rollback and ongoing responsibility
Version the previous and new configurations together with the affected routes and purpose of the change. Before switching, define which functional failures trigger rollback and who can perform it. A limited exception for an evidenced dependency is easier to trace than an unnoticed global relaxation.
On failure, first reverse the responsible change at its owning layer, then perform any necessary cache invalidation and functional retesting. A server rollback cannot simply change already loaded documents, stored HSTS or deleted browser data retrospectively. A new Report-Only policy does not override an old enforced CSP either. Consult the relevant articles for HSTS, Clear-Site-Data and stored state.
Keep dependencies, exceptions and responsibilities current. Framework changes, CDN rules, new third parties or changing browser support can alter the effect. A single successful scan does not provide continuing operational evidence.
How the Web Security Check can contribute
The Web Security Check provides a limited external snapshot. The main analysis examines the response and HTML its test client can reach after redirects. The recorded redirect chain and separate subsidiary requests supplement this view. The test client does not execute the application like an authenticated browser and does not automatically visit every subpage.
On narrow screens, scroll the table sideways.
| Finding group | Reference point in the reviewed product version |
|---|---|
| Main headers, caching and cross-origin fields | The final response received, not a complete route/session matrix |
| SRI | Evaluated HTML tags using the final URL as their base; not verification of every byte actually loaded or dynamic import |
| TLS | A separate connection generally to the final HTTPS host, with limited negotiation/certificate data |
| DNSSEC/CAA and security.txt | The requested host and their documented subsidiary queries; not automatically the host of the final HTML |
| Redirects and cookies | Recorded redirect steps, additional limited redirect probes and received cookie settings; not a complete user journey |
A missing header, a transport failure and an unexamined state are different findings. Likewise, a visible policy does not prove effective browser enforcement, cross-origin isolation, successful report delivery or general legal compliance. Use the check for the appropriate external view and add the operator and browser evidence your protection goal requires. The separate API guides cover CORS and API endpoint security.
Frequently asked questions
Do I need every security header?
No. Choose mechanisms according to response type, functionality and risk. An unnecessary isolation or data-clearing header can break the application.
Should everything live in the application or in the CDN?
Assign responsibility for each rule and response family. Dynamic nonces and static transport rules can have different owners, but must work together.
Why is an nginx header missing on a subroute?
Check inheritance and status codes. Under the standard model, local add_header directives can replace the inherited group; newer versions offer an explicitly configurable inheritance model.
Is CSP Report-Only enough before release?
No. It helps with observation but does not itself block anything. Required functions and deliberate countercases must also work as intended under the policy that will later be enforced.
Can I apply this guide to Shopify?
Distinguish a platform theme, your own Hydrogen storefront and any additional proxy. Their control and support boundaries differ. None guarantees freely configurable headers for every platform response.
Does a good scanner result mean rollout is complete?
Only once the application-specific acceptance criteria are also met. A public retrieval does not know every session, cache, browser and error path.
Sources and review date
Source review: 27 September 2026. The linked specifications and vendor documentation support the rules described. The original local check adds limited Chromium countercases; server and platform examples are not universally tested operational configurations. For the legal context, see web security and compliance.