Two response headers do most of the practical work on a content site: Content-Security-Policy (CSP) limits which resources a page may load, and Referrer-Policy limits how much of your page address travels to other sites when a visitor clicks away or a resource loads. If you run a site for an esports club, a tournament league or any publication with sponsor banners and embedded streams, the safe order is to inventory what your pages load, start CSP in report-only mode, read the reports, and enforce only after the reports go quiet. Referrer-Policy is the simpler of the two and can go first.
What each header is for
MDN's web security overview (Opens in a new tab) lists a short set of practices for site owners: serve everything over HTTPS, set a Content Security Policy, limit access to cookies, use Subresource Integrity for scripts loaded from other origins, and follow sound operational security. It also points to the HTTP Observatory scanner for checking whether a site follows those practices. Headers are one layer of that set, not a substitute for patching, strong passwords or sanitizing input. MDN's CSP guide makes the same point: setting a CSP is not an alternative to sanitizing input, and sites should do both to get defense in depth against cross-site scripting.
Table: the jobs of the headers covered here.
| Header or directive | Job | Where you see the effect |
|---|---|---|
| Content-Security-Policy | Restricts which resources can load and run | Browser console and violation reports |
| Content-Security-Policy-Report-Only | Reports violations without blocking | Your report endpoint |
| Referrer-Policy | Controls how much address information the Referer header carries | Referral data on other sites and in analytics |
| frame-ancestors | Controls who may embed your pages in frames | Partner pages that embed you |
The MDN pages describe these headers as security and privacy controls. They make no ranking claim, so treat the work as risk reduction rather than an SEO tactic.
Start with Referrer-Policy: the smaller decision
According to MDN (Opens in a new tab), the Referrer-Policy response header controls how much referrer information, sent in the Referer header, is included with requests. The header name Referrer-Policy is spelled with two r letters, while the request header it governs is the historically misspelled Referer. When no policy is specified, or when the value is invalid, the default is strict-origin-when-cross-origin. Before that change the default was no-referrer-when-downgrade.
Table: Referrer-Policy values as MDN describes them.
| Value | What is sent |
|---|---|
| no-referrer | No Referer header at all |
| no-referrer-when-downgrade | Origin, path and query when the security level stays the same or improves. Nothing from HTTPS to HTTP |
| origin | Only the origin, for example https://example.com/ |
| origin-when-cross-origin | Full address for same-origin requests, only the origin across origins |
| same-origin | Full address for same-origin requests, nothing across origins |
| strict-origin | Only the origin on HTTPS to HTTPS, nothing to a less secure destination |
| strict-origin-when-cross-origin (default) | Full address for same-origin, only the origin across origins on HTTPS to HTTPS, nothing to a less secure destination |
| unsafe-url | Origin, path and query on every request |
MDN warns about unsafe-url specifically: it leaks potentially private information from HTTPS resource addresses to insecure origins, so its impact deserves careful thought. Think of a registration link such as a tournament signup address that carries a team code in the query string. With a permissive policy that code could travel to every third party the page contacts.
Choosing a value
- Your addresses carry no private data: the default is a reasonable fit. Writing it explicitly records the intent and protects against a future change of browser defaults.
- Your addresses carry codes, tokens or identifiers: consider same-origin or no-referrer. The cost is that partner sites and analytics tools see less about where your visitors came from.
- You want to share only the site name with others: origin or strict-origin.
- unsafe-url: avoid unless you can name the reason.
Page-level and link-level controls
MDN describes three alternatives to the header. A meta element with name referrer applies to the whole document, and its values are written with a dash (no-referrer). The referrerpolicy attribute works on a, area, img, iframe, script and link elements. The noreferrer link relation works on a, area and link elements, and is written without a dash. MDN calls out that difference in spelling because it is easy to mix up. A comma-separated list of fallback values is accepted only in the HTTP header, not in the attribute.
Take stock before writing a CSP
The hard part of CSP is knowing what your pages really load. Example scenario: a club site embeds a live stream, a chat widget, sponsor banners, a match schedule from another service, a merchandise store frame, a newsletter form and an analytics script. Each lives on a different origin, and each needs a decision.
Practical suggestion: open the browser developer tools, load a representative page with cookie consent given and not given, and record every origin by resource type: scripts, styles, images, fonts, frames and connections. Add inline scripts and inline event handlers written by your theme or plugins, because those are what a strict policy fights hardest. The same inventory feeds the measurement approach in the third-party scripts performance audit, so one pass serves security and speed.
| Inventory item | Question to answer |
|---|---|
| Own scripts and styles | Can inline code be moved to files? |
| Ad and tag manager scripts | Which further origins do they pull in? |
| Embedded frames | Which providers do you embed? |
| Forms | Where do they submit? |
| Admin and login pages | Do they need a separate policy? |
CSP in plain terms
MDN's CSP guide (Opens in a new tab) says the primary use case is to control which resources, in particular JavaScript resources, a document may load, mainly as a defense against cross-site scripting. A policy is delivered in the Content-Security-Policy response header, and MDN says it should be set on all responses to all requests, not only the main document.
| Directive | As MDN describes it |
|---|---|
| default-src | Fallback policy for resource types not listed separately |
| script-src | Allowed sources for JavaScript |
| style-src | Allowed sources for CSS |
| img-src | Allowed sources for images |
| object-src 'none' | Blocks object and embed elements |
| base-uri 'none' | Stops the base element from changing the base address |
| frame-ancestors | Controls embedding of your page, a more flexible replacement for X-Frame-Options |
| upgrade-insecure-requests | Upgrades HTTP addresses to HTTPS, not a substitute for HSTS |
MDN's simplest example allows everything from the site's own origin and images from one extra origin. A policy that tight breaks a page with streams, sponsors and widgets on day one, which is exactly why report-only mode exists.
Report-only mode: how to begin
MDN states that to ease deployment, a CSP can be deployed in report-only mode through the Content-Security-Policy-Report-Only header. The policy is not enforced, but violations are sent to the reporting endpoint named in the policy. If both headers are present, the Content-Security-Policy policy is enforced while the Report-Only one only generates reports. That lets you trial a stricter revision while the current policy keeps protecting visitors. One limit to remember: a report-only policy cannot be delivered in a meta element, and MDN adds that the meta route does not support all CSP features.
- Write a first policy from your inventory and publish it as Report-Only.
- Collect reports for a period you choose. MDN gives no recommended duration.
- Sort each reported violation into expected, unnecessary or unexplained.
- Adjust and republish in report-only mode.
- When reports settle, move the policy to the enforcing header.
Collecting the reports
MDN recommends the Reporting API. You declare an endpoint name in a Reporting-Endpoints header and reference it with the report-to directive. Reports arrive as POST requests with the content type application/reports+json and an object whose type is csp-violation. The older report-uri directive sends POST requests with application/csp-report. MDN describes that approach as deprecated but advises declaring both until report-to is supported in all browsers. Your endpoint must accept both content types. If you use a hosted collector, remember that reports can contain page addresses your visitors viewed, so check its retention and privacy terms. That last point is a practical suggestion.
Triage rule for reports
Practical suggestion: expected violations are resources you know and need, so add them. Unnecessary ones are leftovers, such as a widget from last season that a plugin still calls, so remove the call rather than allow the origin. Unexplained ones need investigation before anything is allowed, because the point of a policy is to stop unexpected code. Group the first week of reports by origin and directive. When one origin appears on many pages, one central rule is better than page-by-page fixes.
Nonces, hashes and unsafe-inline
With a nonce, the server generates a random value per response and writes it both into the header and into the nonce attribute of the script element. MDN says the nonce must differ for every HTTP response and must not be predictable, which means the server cannot serve static HTML and needs templating. With a hash, you compute a SHA-256, SHA-384 or SHA-512 digest of the script, encode it in Base64 and add the prefix. External scripts also need an integrity attribute for the method to work. Both the policy and the content can stay static.
On unsafe-inline, MDN says developers should avoid it because it defeats much of the purpose of having a CSP. When a directive contains nonce or hash expressions, browsers ignore unsafe-inline. unsafe-eval keeps working even then, and MDN says to avoid it as well. MDN's strict example combines a nonce-based script-src with object-src 'none' and base-uri 'none'.
| Situation | Fit | Watch for |
|---|---|---|
| Pages rendered by a server template | Nonce per response | A cached page cannot reuse a nonce safely |
| Fully static pages | Hashes | Digest must be refreshed whenever the script changes |
| Many inline onclick handlers | Refactor to addEventListener first | MDN suggests refactoring inline handlers and javascript: addresses |
| Third-party script that loads more scripts | Consider strict-dynamic | MDN says it makes the CSP less secure |
The caching note is an inference from the nonce rule, and MDN does not discuss page caches in this context. Test your cache or plugin setup before relying on it.
Ad and analytics scripts under a CSP
This is where publishers and sponsor-funded sites struggle. An ad or tag manager script can bring in more scripts, images and frames from other origins. MDN says allowlist CSPs are hard to get right and that policies can inadvertently allow unsafe domains. Its preferred route is a nonce- or hash-based strict CSP, with strict-dynamic as a convenience that, in MDN's words, makes the CSP less secure because scripts it trusts may create further script elements.
Decision rule: on a page with a third-party script, observe in report-only mode which origins it contacts. Then pick one of three paths.
- Give the script a nonce and use strict-dynamic. Less list maintenance, weaker protection as MDN warns.
- List the origins in script-src, img-src and frame-src. You own the list, and a provider adding an origin can break the page.
- Isolate the ad slot in its own page or frame and keep the main page policy tight.
Consent adds a wrinkle. Scripts that load only after consent still need to be covered, so the policy must work in both states. See the cookie consent and Consent Mode guide for the measurement side and the ads and Core Web Vitals balance for the speed side.
Questions for sponsors and ad vendors
Ask for written answers. Which origins does the script load from, and will you be told when that list changes? Is there a nonce-friendly loading option? Does the script rely on eval? Does the vendor publish a CSP example? MDN has no vendor-specific guidance, so the vendor's own documentation is the authority here.
Frames: who embeds whom
Two directives get confused. frame-src decides which outside frames your page may embed, such as a stream or a map. frame-ancestors decides who may embed your page. MDN says that unless you need your site to be embeddable, you should set frame-ancestors to 'none'.
Example scenario: the league wants one partner site to show its standings table in a frame. Instead of 'none', the policy lists only that partner's origin. For the stream on its own pages, the league adds the streaming provider's origin to frame-src. Confirm origins against the provider's current embed documentation, because no fixed list belongs here.
The rest of MDN's checklist
Beyond CSP and Referrer-Policy, MDN's security page lists these practices. Set SameSite to Strict if possible, otherwise Lax. Set Secure and HttpOnly where possible. Minimize the lifetime of cookies that represent logged-in users. Use Subresource Integrity for scripts loaded from CDNs. Decide when other origins may make requests to your site and use fetch metadata to enforce it. Control access to source code, handle secrets safely and watch dependencies.
| Practice | How to check it |
|---|---|
| HTTPS for everything | Mixed-content scan, see the mixed content and HSTS checklist |
| Cookie attributes | Cookie panel in browser tools |
| SRI on external scripts | integrity attribute on the script tag |
| Headers present | HTTP Observatory scan and response headers |
MDN's CSP page also says upgrade-insecure-requests is not a substitute for the Strict-Transport-Security header, so HSTS remains its own task.
Publishing, testing and rolling back
Headers can be added in server configuration, in a CDN or protection layer rule, or in application code. The choice depends on your hosting plan, and no provider steps are given here. Define each header in one layer only, because duplicates in two layers can produce policies you did not intend. That is a practical suggestion. Protection layers and bot rules can also touch response headers and crawler access, a topic for the guide to bad bot traffic and Googlebot.
Pre-launch test list
- Is the header visible on the live response?
- Do the home page, an article, a team page, a form page and the login page each load without console violations?
- Do the stream embed, analytics and ad slots work?
- Were both consent states tested?
- Was it tried on mobile and in at least two browsers?
- Does a live test in Search Console URL Inspection show the expected content?
The last item is a practical suggestion: a blocked script can leave the rendered page incomplete, and comparing what Google fetched with what a browser shows catches that early.
| Symptom | First check | Rollback or fix |
|---|---|---|
| Stream embed missing | frame-src violation in the console | Add the provider origin or switch the policy back to report-only |
| Analytics drop | script-src and connect-src violations | Allow the measurement origin |
| Form will not submit | form-action and connect-src | Allow the form destination |
| Sponsor banner blank | img-src and frame-src | Verify the sponsor's origins, then add them |
| Referral sources vanish from reports | The Referrer-Policy value | Return to a less restrictive value |
Write the rollback before you ship: keep the change to one configuration line or rule, save the previous version and name who is allowed to revert. Enforcing first on one section, such as the blog, limits the damage of a mistake. Keep the report endpoint running after enforcement, because a new sponsor, widget or plugin will bring violations back. Record each new third-party component's origins before it goes live and rerun the test list.
Staging and ownership
Practical suggestion: try every header change on a staging copy first, with the same sponsor embeds and consent banner as production. Name one owner for the header configuration, because a policy edited by several people can collect unexplained exceptions. Keep a short change log with the date, the old value, the new value and the reason. When a sponsor contract ends, remove its origins from the policy at the same time as its banner, so the allowlist does not grow into a list of forgotten partners.
Reading the console during tests
A blocked resource appears as a console message naming the directive that refused it. In report-only mode the same message appears but the resource still loads, which is why testing in that mode is safe. Practical suggestion: reload each template with the console open, note the directive named in each message and map it to your inventory, then decide whether the origin belongs in the policy or the resource should go. Repeat after every change to the theme, a plugin or an embed code.
Where headers stop
Headers narrow what a compromised or mistaken page can do. They do not fix a weak admin password, an outdated plugin or an exposed backup. For the wider hosting and account side, work through the website security and hosting checklist for publishers. Also keep expectations accurate with stakeholders: nothing in the MDN pages promises a search ranking benefit from these headers.
Example scenario: the club ships Referrer-Policy in week one and CSP in report-only mode in week two. Reports are triaged in week three, enforcement starts on the blog section after that, and the schedule and store pages follow once their embeds are covered. This timing is a suggestion. MDN gives no durations.
Frequently Asked Questions
Does a CSP improve my search ranking?
The MDN pages describe CSP and Referrer-Policy as security and privacy controls and make no ranking claim. Treat them as risk reduction.
Can I enforce a CSP right away?
Technically yes, but MDN recommends report-only mode to ease deployment. A wrong enforced policy can block scripts, frames and images.
Does report-only mode work in a meta element?
No. MDN says a report-only policy cannot be delivered in a meta element, so Content-Security-Policy-Report-Only must be an HTTP header.
What happens if I set no Referrer-Policy?
MDN says the default is strict-origin-when-cross-origin when no policy is specified or the value is invalid. The older default was no-referrer-when-downgrade.
How do I allow ad scripts in a CSP?
MDN explains that allowlists are hard to get right and prefers nonce or hash based policies, with strict-dynamic as a trade-off that reduces protection. Which origins an ad network needs is in the vendor documentation.
Is unsafe-inline acceptable?
MDN says developers should avoid it because it defeats much of the purpose of a CSP. Browsers ignore it in a directive that also contains a nonce or hash.
Conclusion
Security headers are not a magic shield. They do a few specific jobs: CSP limits what may load and run, Referrer-Policy trims the address information that leaves your site, and frame-ancestors controls who can embed you. The safe route starts with an inventory, uses report-only mode to collect evidence, treats ad and analytics scripts as their own decision and keeps every change reversible. Publish the headers as part of your defense, not as a ranking promise, and recheck page functions and referral reports after each change.
Once the foundation is solid, you can turn to visibility work. For link building support, see our backlink packages.
Post a comment
Please log in to leave a comment. Login