Skip to content
Blog

Cloudflare JavaScript Detections: First Request and CSP

Why the Cloudflare JavaScript Detections field reads false or missing on real browsers, and how to fix first-request, CSP nonce, no-transform and ETag causes.

Centinel Analytica
Published
Cloudflare JavaScript Detections: First Request and CSP

The Cloudflare JavaScript Detections field is false or missing on real browsers for four common reasons: the request was the first one a client made, a Content Security Policy blocked the script, the origin sent Cache-Control: no-transform, or the script never ran far enough to set its cookie. Treat false as a missing signal, not proof of automation.

This is an operator runbook. It separates injection, execution, cookie and next-request evidence so you can tell which link broke, then walks through the configuration fixes. It doesn't cover picking a challenge type. For that, see the comparison of Cloudflare Managed Challenge vs JavaScript Challenge.

Short answer: why the JavaScript Detections field is false or missing

The field reads false when Cloudflare has no completed JavaScript Detections result for that request. That's a statement about evidence, not about the visitor.

The first request from a new client will generally have no JavaScript Detections data, so the field is false (Cloudflare Docs). A CSP that blocks the injected script, an ETag-related caching quirk, or an origin Cache-Control: no-transform header can also leave you with nothing to evaluate. With no-transform, Cloudflare doesn't inject the script at all, so you get a missing signal rather than a failed one.

The practical rule: never use this field on a first landing request, and never read false as "this is a bot." A WAF rule that blocks on false will block real people who arrive from a search result or a bookmark.

What JavaScript Detections does, and what your plan lets you enforce

JavaScript Detections is a lightweight, invisible script snippet that Cloudflare inserts into your HTML pages. It's separate from Challenge Pages and Turnstile, and it runs without showing the visitor anything (Cloudflare Docs).

Availability and enforcement are different things. Bot Fight Mode customers have it enabled automatically and can't disable it. But to write WAF custom rules on the cf.bot_management.js_detection.passed field (or the Workers equivalent), you need an Enterprise Bot Management subscription. If you're unsure what your contract includes, use the Cloudflare bot protection pricing and Enterprise quote checklist before you design rules around the field.

Two more facts shape your expectations. JavaScript Detections doesn't guarantee a specific bot score. And customers who enabled Enterprise Bot Management before June 2020 don't have it on by default unless it was specifically requested, so check that first if the field never populates anywhere.

Four separate pieces of evidence: injection, execution, cookie, next request

Diagnose one link at a time. A missing signal is a chain with one broken link, and each link has its own evidence you can see in a browser.

  1. Injection. Did Cloudflare add the script tag to the HTML response? View source on the HTML document and look for a same-origin challenge-platform script reference. If it's absent, nothing downstream can work.
  2. Execution. Did the browser run it? Open the console and network panel. A blocked script shows as a CSP violation or a failed request, not as a missing tag.
  3. Cookie. Did execution leave a cookie? Check the Application or Storage panel for a Cloudflare cookie set after the page loaded. A script that ran but couldn't set a cookie (blocked storage, a privacy extension) produces no usable result.
  4. Next request. Did the following request to your site carry that cookie? The field is evaluated on requests after the earlier HTML load, so inspect a second navigation or an XHR, not the landing request.

If you also use the executeOnce callback in the API, remember that a success callback only means the script executed. It says nothing about whether the visitor is human or a bot. Separately, JavaScript Detections for API traffic is a zone setting that controls whether Cloudflare consumes results created through the JavaScript Detections API, and it can be off (Cloudflare Docs). A working callback with that setting off still gives you an empty field.

First-request rule: scope WAF examples to browser routes after an earlier HTML load

Only evaluate the field on requests that happen after a browser has already loaded an HTML page from your site. On the very first request, the field is false by design.

A safe pattern has three parts:

  • Scope to browser routes. Apply the rule to paths that real browsers reach after a page view: add-to-cart calls, search suggestions, form posts, paginated content fetches. Don't apply it to the entry URLs people land on.
  • Exclude traffic that never loads HTML. Native apps and websocket upgrades don't receive an injected script, so they'll read false forever. Exclude them explicitly.
  • Challenge, don't block. Use Managed Challenge as the action so a real visitor with a missing signal can recover. A blanket block on false turns every injection or CSP problem into an outage.

A concrete before and after: a rule that says "block when js_detection.passed is false" on /product/* will fire on every shopper who arrives from Google. Scope it to /api/cart/* with a Managed Challenge action and it only fires after that shopper has already loaded an HTML page. For how to layer this with scoring, see Cloudflare bot score: set thresholds by route.

CSP, no-transform and ETags: the three config causes of a missing signal

Three origin-side settings account for most persistent false results on real browsers. Check them in this order.

Content Security Policy. If you run a CSP, it must allow the same-origin challenge-platform scripts Cloudflare injects. Deliver your nonce configuration through the CSP response header. A nonce supplied through a meta tag isn't supported for this purpose. Don't "fix" a blocked script by adding unsafe-inline. That weakens the policy you built to stop cross-site scripting, and it isn't needed. Test with the policy in report-only mode first and read the violation reports.

Cache-Control: no-transform. If the origin response includes Cache-Control: no-transform, Cloudflare doesn't inject the script. The result is a missing signal, not a false one that failed. Before you remove the directive, find out why it was added. Image optimizers, proxies and compliance requirements sometimes depend on it. Remove it only on the HTML responses where you want injection, and only after that review.

ETags. Enabling JavaScript Detections strips ETags from HTML responses where the script is injected (Cloudflare Docs). That's expected behavior, not a bug, but it changes conditional-request behavior. If your caching or monitoring relies on HTML ETags, expect revalidation traffic to change and adjust for it.

None of these three produces an error you'll see in the dashboard. They show up as a quiet absence of signal, which is why the evidence sequence above matters.

Browser network, cookie and CSP troubleshooting sequence

Run this in a clean browser profile with extensions off, then repeat with your usual profile.

  1. Load an HTML page and view source. Confirm a same-origin challenge-platform script tag exists. If not, check no-transform on that response, then confirm the feature is enabled for the zone and your Bot Management start date.
  2. Open the console. Look for CSP violation messages naming the script. If present, adjust the header-delivered policy rather than loosening it globally.
  3. Open the network panel. Confirm the script request returns successfully from your own hostname, not a blocked or cancelled request.
  4. Open the cookie panel. Confirm a Cloudflare cookie appears after load. If the script ran but no cookie exists, check for blocked storage or a cookie-clearing extension.
  5. Navigate to a second HTML page or fire a fetch. Confirm the request carries the cookie. This is the request on which the field is meaningful.
  6. Check the zone setting for API results if you rely on the executeOnce callback.

Don't paste cookie values, tokens or session identifiers into tickets or logs while you do this. Record only presence, name and timing.

Test matrix: first landing, next HTML navigation, nonce policy, no-transform, no-JS traffic

Build a small matrix and run it before every rule change. Each row has an expected result, and a mismatch tells you which link broke.

ScenarioExpected field stateWhat a mismatch points to
First landing request, no cookieFalse (no data)Rule is scoped to entry URLs; move it
Next HTML navigation, normal browserPopulated after the script runsInjection, CSP or cookie failure
Strict CSP with header-delivered noncePopulatedScript blocked; fix the policy, not unsafe-inline
Response with Cache-Control: no-transformMissing (no injection)Directive on the HTML route
Legitimate no-JS traffic (feed readers, accessibility tools, monitors)False or missingExpected; must not be blocked

The last row is the one teams skip. Real, approved clients that don't execute JavaScript will always look like false. Decide in advance what they get, and exempt them by route or by other evidence instead of discovering them through complaints.

Enforcing safely: Managed Challenge, API results setting and when a layer beside Cloudflare helps

Enforce in stages. Start with the rule in log or simulate mode and review how many real, completed sessions it would have hit. Move to Managed Challenge on the scoped browser routes. Reserve block for cases where you have independent evidence beyond this one field.

Keep the API results setting in mind. If you depend on API-originated results, confirm the zone is consuming them, or your rule will see nothing regardless of how good the callback looks.

One field from one script is a thin basis for a verdict. Centinel Analytica is bot and AI crawler protection that tells real visitors from scrapers and applies allow, challenge or block policy through your existing CDN or edge. It works alongside Cloudflare, so your CDN and WAF stay in place. If your JavaScript Detections results are too ambiguous to enforce on, a layer beside Cloudflare that weighs the request, browser and connection together gives you a second source of evidence for the same routes. Compare options in Cloudflare Bot Management alternatives: architecture first.

Treat false as missing, then test the chain before you enforce

A false JavaScript Detections result tells you evidence is absent, and absence has four possible causes you can check one by one: injection, execution, cookie and next request. Fix the CSP header, the no-transform directive and the ETag expectations, scope rules to browser routes after an earlier HTML load, and challenge rather than block. If you want a second opinion on your own traffic, see how Centinel Analytica applies policy beside Cloudflare on your routes.

Frequently asked questions

Why is cf.bot_management.js_detection.passed false for real users?

Most often it is the first request from a new client, which generally has no JavaScript Detections data. A CSP blocking the injected script, a no-transform header on the origin response, or a script that ran without setting its cookie also leave the field false. Treat it as missing evidence, not proof of automation.

Does Cache-Control no-transform break JavaScript Detections?

Yes. When the origin response includes Cache-Control: no-transform, Cloudflare does not inject the script, so the signal is missing rather than failed. Before removing the directive, find out why it was set, then remove it only from the HTML responses where you want injection.

Do I need Enterprise Bot Management to use the JavaScript Detections field in WAF rules?

Yes. Writing WAF custom rules on the JavaScript Detections field requires an Enterprise Bot Management subscription, even though the feature itself is enabled automatically for Bot Fight Mode customers. Confirm your entitlement before building rules, and consider a layer like Centinel Analytica beside Cloudflare if the field alone is too thin to enforce on.

Should I block requests where JavaScript Detections is false?

No. Use Managed Challenge on browser routes reached after an earlier HTML load, and exclude native apps and websockets. A blanket block on false hits first-time visitors and approved no-JS clients. Centinel Analytica applies allow, challenge or block policy through your existing CDN using several kinds of evidence, not one field.