Content security policy and SEO: is your own header blocking Google's renderer?
Your content security policy is enforced inside Google's renderer, because that renderer is a headless Chromium. Here is the three-step check that rules CSP in or out as the reason your page renders empty, and the point where you should stop.

Your content security policy is enforced inside Google's renderer, because that renderer is a Chromium. If your page builds its text with JavaScript, and the script that builds it comes from a host your policy never listed, the renderer hits the same wall a visitor's browser would. Nothing returns an error. The page still answers 200. This chapter is the check that rules that cause in or out, and the point at which you should stop looking.
Why a content security policy reaches Google's renderer too
Google describes the rendering path in plain sentences. Understand the JavaScript SEO basics, retrieved 2026-09-01, states that "All pages with a 200 HTTP status code are sent to the rendering queue, no matter whether JavaScript is present on the page", and that "Once Google's resources allow, a headless Chromium renders the page and executes the JavaScript". The same page opens with "While Google Search runs JavaScript with an evergreen version of Chromium". One machine, named twice: a current Chromium, executing your scripts.
A Chromium enforces CSP. That is the entire purpose of the header. MDN's Content-Security-Policy reference, retrieved 2026-09-01, describes it as a way for site owners "to control resources the user agent is allowed to load for a given page" — the user agent, whichever user agent that happens to be. Put the two statements side by side and you get the claim this chapter rests on: a script your policy refuses in a visitor's browser is refused in the renderer too.
That second step is our inference, not documentation. Google has published nothing specific about CSP and rendering, and we have not run a controlled experiment — two identical pages, one behind a blocking policy, both submitted and compared over time. Anyone who tells you Google confirmed that CSP affects indexing is quoting a sentence that does not exist. Treat this as a cause worth excluding, not a mechanism anyone has proved.
A crawler that never runs scripts cannot be blocked by your content security policy. The one client it can block is the one deciding whether your text exists at all.
When this is worth five minutes, and when it is not
The check earns its time on exactly one kind of page: one where the words a reader sees are produced by JavaScript after the document arrives. Fetch your page with curl and read what comes back. If the article is already in that HTML, no policy of yours can take it away — CSP governs what the browser is allowed to load, and it has no opinion about text the server already sent. Server-rendered sites can close this chapter here.
The same logic sets the ceiling. AI crawlers that read HTML without executing it are outside your policy's reach entirely, which is a separate problem covered in can AI crawl JavaScript. So this check is narrow by design: it can tell you that your content security policy is a plausible reason Google's renderer came up short, and it cannot tell you that the policy is the reason. If your scripts all load and the page still renders empty, the cause is somewhere else and this chapter has nothing more to offer.
Three steps to check it on your own site
Each step has one thing you should be able to state out loud before moving on. If you cannot state it, repeat the step rather than continuing.
- Read the policy your server actually sends. Request the page and look at the response headers, not the HTML. Then look again inside the document, because a policy declared with
<meta http-equiv>never appears in the headers at all. You are through this step once you can paste the full policy string somewhere, or say with confidence that there is no policy on this URL — in which case you are finished, and the cause is elsewhere. - Line the allowlist up against the hosts your page really pulls from. Pull every script host out of the delivered HTML, then compare that set against the sources named in
script-src— or, ifscript-srcis absent, againstdefault-src, which MDN documents as the fallback for all other fetch directives. Do the same forconnect-srcand the API your front end calls for its content. This step ends with a written list of hosts in one column and allowed or not allowed in the other. A single "not allowed" on a host that carries your text is your answer. - Confirm it in a client that reports violations. Open the page in a browser with the console visible and reload; a blocked resource prints a violation naming the directive that stopped it. Then check the same URL in Search Console's URL Inspection tool, because that reads Google's own render. Google's Fix search-related JavaScript problems, retrieved 2026-09-01, says of the Rich Results Test and URL Inspection: "You can see loaded resources, JavaScript console output and exceptions, rendered DOM, and more information." You are done when the rendered DOM either contains your article or visibly does not, and you know which script is missing.
The command and the directive table
Paste this against one URL of yours. It prints the policy, the in-document declaration, and every script host the page references, which is steps one and two in a single pass.
URL="https://example.com/"
echo "--- policy sent as a response header"
curl -sSI "$URL" | grep -i '^content-security-policy'
echo "--- policy declared inside the document"
curl -sS "$URL" | grep -io '<meta[^>]*content-security-policy[^>]*>'
echo "--- script hosts the page actually references"
curl -sS "$URL" \
| grep -Eo 'src="https?://[^/"]+' \
| sed 's/src="//' | sort -u
If your edge serves different headers to different clients, run the first command a second time with your crawler user agent and compare. Now read the directives you found against the table. It covers the ones that decide whether a rendered page has text in it, plus two that people write far more often than they need to.
| Directive | What it controls | If written too narrow | HTML-only crawlers |
|---|---|---|---|
script-src | Valid sources for JavaScript and WebAssembly resources | The bundle that builds your text never loads | No effect — they do not execute it |
default-src | Fallback for all other fetch directives | Everything you forgot to name is refused at once | No effect |
connect-src | URLs that can be loaded using script interfaces | Scripts run, the content API call fails, the page renders its empty state | No effect |
style-src | Valid sources for stylesheets | Layout breaks; the text itself usually survives | No effect |
frame-ancestors | Which parents may embed the page | Nothing the renderer needs | No effect |
upgrade-insecure-requests | Treats insecure URLs as though they were HTTPS | Nothing — it rewrites rather than refuses | No effect |
Directive definitions are quoted or condensed from the MDN reference above, retrieved 2026-09-01. The last two rows are the useful part of the table: most of what sits in a real-world policy has no bearing on whether a rendered page contains words.
Three ways this check goes wrong
All three produce a confident answer from the wrong evidence.
- Reading a
<meta>policy as though it covered the whole document. The CSP Level 3 specification, retrieved 2026-09-01 at w3c.github.io/webappsec-csp, says authors "are strongly encouraged to place meta elements as early in the document as possible, because policies in meta elements are not applied to content which precedes them", and spells out that scripts loaded before a meta-delivered policy "will not be blocked". So the policy you read in the source is not necessarily the policy that applied to the script you are investigating. MDN adds that the header form "should be set on all responses to all requests, not just the main document", and that the meta form "does not support all CSP features". - Treating report-only as the live policy. MDN's CSP guide, retrieved 2026-09-01, is explicit: in report-only mode "the policy is not enforced, but any violations are sent to the reporting endpoint specified in the policy". A site can carry both headers at once, and only the enforcing one blocks anything. Grep for the header name without checking its suffix and you will spend an afternoon on a policy that has never stopped a single request.
- Testing an environment that does not have the policy. CSP is frequently added at the edge or the reverse proxy in production only, so staging and your laptop run without it and reproduce nothing. Run every step of this check against the public URL, the same one you would submit for indexing.
How often does a policy exist at all
Some scale, offered as background rather than as a finding. On 2026-09-01 we requested 27 comparable homepages once each from an exit in Osaka, without executing JavaScript. Seventeen returned a content-security-policy response header, one declared its policy with <meta http-equiv>, and nine sent no policy in any form. The longest header was 7,702 bytes, on www.notion.com; the shortest was 22 bytes, on railway.com, which sends nothing but frame-ancestors 'self'. Of the seventeen, fourteen still allowed 'unsafe-inline'. Those numbers describe 27 sites on one day and predict nothing about yours — what they do suggest is that a policy tight enough to break a render is rarer than the amount of writing about CSP would lead you to expect.
Common questions
Does Googlebot respect Content-Security-Policy?
The rendering stage runs a headless Chromium, and Chromium enforces CSP, so the reasonable expectation is yes. Google has not documented the behaviour, and we have not tested it in isolation. That gap is why this chapter is framed as excluding a cause rather than diagnosing one.
Will a strict CSP hurt my rankings?
Not by being strict. It can cost you if a directive stops a script that your page needs in order to have any text, and only on pages that depend on scripts for their text. A tight content security policy on a server-rendered site costs you nothing in search.
Is Content-Security-Policy-Report-Only safe to leave on?
It blocks nothing, which is the point of it and also the trap. Use it to see what a future policy would break before you enforce it, and never read its presence as evidence that a policy is live.
My policy is in a meta tag instead of a header. Does that change the check?
It changes step one: the policy will not show up in curl -I output, so you have to read the document. It also changes what you can conclude, because a meta-delivered policy applies only to what follows it in the source, and report-only cannot be delivered that way at all. If you have the option, move the policy to a response header before debugging anything.
Where should I look if the policy turns out to be clean?
Back one layer, to whether your text depends on JavaScript in the first place. Check what AI crawlers get from your URL, then compare that against the character counts in what AI crawlers get from JavaScript sites. A page that arrives empty for a plain fetch has a problem no policy change will fix.
Part of the QueryWin handbook · Level 2


