Skip to content

Browser Bot Detection Script

The browser script is the fastest install that works on any website. It catches headless browsers, automation, browser agents, and LLM referrals from clients that execute JavaScript.

Create a free account → · Compare all install methods

  1. In the WebDecoy dashboard setup checklist, open Install the detection script and choose Copy. If the checklist is closed, open Detection Scripts, create a script for the selected site, and choose Copy snippet.
  2. Paste the generated line into the shared layout for your site, just before </head>.
  3. Deploy and load one page that contains the tag. The checklist waits for the first sensor event and confirms when it arrives.

The generated line looks like this, but your data-aid and data-sid values are unique:

<script
async
src="https://cdn.webdecoy.com/bot-detection/v1/bot-detection.min.js"
data-aid="your-property-uuid"
data-sid="your-scanner-uuid">
</script>

To confirm the install, open Sensors and look for the Detection Script row, or follow the installation verification checklist.

A detection script is a JavaScript-based detection system that runs in visitors’ browsers. It analyzes browser characteristics and behavior to identify automated tools, headless browsers, and bots.

For capabilities, coverage, and deployment options, see the Bot Scanner product overview.

The detection script uses a two-phase approach:

Page Load (0ms)
│
├── Phase 1: Immediate Detection
│ ├── Basic bot signals (webdriver, headless, automation globals)
│ ├── CDP artifact detection
│ ├── Chrome object, iframe and permission consistency
│ ├── AI crawler user agents and AI browser extensions
│ └── Send detection if client score >= 20
│
▼
After 5 seconds (or when the page unloads)
│
├── Phase 2: Behavioral Analysis
│ ├── Mouse movement patterns
│ ├── Click timing analysis
│ ├── Scroll speed patterns
│ ├── Synthetic (untrusted) events
│ ├── Canvas, WebGL and audio fingerprints
│ └── Send behavioral update if client score >= 10
│
▼
Final Score Calculated

Why two phases?

  • Phase 1 catches bots that leave immediately
  • Phase 2 adds behavior and fingerprint evidence for visitors that stay
  • Both phases feed the same detection record

Separately, the script sends a small install heartbeat once per browser session so a healthy install on a site with only human traffic still shows as reporting.

Everything here requires the client to execute JavaScript. That is the defining constraint of this install method.

Detection Type What It Catches
WebDriver Selenium, Puppeteer, Playwright automation
Headless Browsers Chrome Headless, PhantomJS, Firefox Headless
AI Browser Agents Claude Computer Use, Stagehand, Browser Use (agents driving a real browser)
AI Browser Extensions Claude, ChatGPT, Perplexity sidebars
Browser Anomalies Inconsistent navigator properties
Behavioral Patterns Non-human mouse movements, instant form fills
Fingerprint Mismatches Canvas, WebGL, font rendering inconsistencies
CDP Artifacts ChromeDriver, Selenium, Puppeteer markers
Environment Integrity MessageChannel, API mocking detection
LLM Referrals Humans arriving from ChatGPT, Perplexity, Claude
Not Detected Why
Googlebot, Bingbot The crawl pass is a plain HTTP fetch. Rendering is a separate, deferred, sampled pass, and most crawls never execute your page’s JavaScript.
GPTBot, ClaudeBot, CCBot, PerplexityBot These fetch HTML directly and do not execute JavaScript at all.
curl, Python requests, Go http, Scrapy No JavaScript engine.
UA impersonators A client spoofing Googlebot never runs the script, so the script never sees it to check.

The script does contain user-agent patterns for these crawlers, which is why older versions of this page listed them. Those patterns only ever match if the crawler executes the script, which these crawlers do not. To detect them you need a server-side sensor.

Detection Script vs. Server-Side Detection

Section titled “Detection Script vs. Server-Side Detection”

These are complements, not alternatives. Most sites should run both.

Aspect Detection Script (Client) Server-Side Detection
Where it runs Visitor’s browser Your server or edge
Requires JavaScript Yes (no JS, no detection) No
What it sees Browser internals, behavior Raw HTTP request, real IP
Sees non-JS crawlers No Yes
Detection depth Deep browser analysis Headers, IP, UA, path
Bypass difficulty Harder to evade Easier to spoof, but verifiable via rDNS
Best for Headless browsers, AI agents, extensions Crawlers, scrapers, scanners, impersonators

  1. Navigate to Detection Scripts

    • Click Detection Scripts in the sidebar, under Countermeasures
  2. Click “Create Scanner”

    • The Create Detection Script dialog opens
  3. Configure the Scanner

    Field Description Example
    Scanner Name Internal identifier “Main Website Scanner”
    Endpoint Domain One of your verified domains that detection data is sent to “www.example.com”
  4. Click “Create Scanner”

    • Detection script is created with a unique ID
  5. Install the Snippet

Detection scripts have no per-scanner tuning. Every script runs the full detection suite: automation and headless-browser detection, AI-crawler identification, behavioral analysis, and browser fingerprinting. The only per-script controls are the name, the reporting domain, and the Status toggle. (The edit dialog also shows a Tier field left over from the old lite/pro split; every script runs the same code.)


Detects browser automation frameworks:

Framework Detection Method Client score
Selenium, WebDriver navigator.webdriver === true +40
Puppeteer __puppeteer global or pptr_* properties +35
Playwright __playwright / __pw_manual globals or Playwright in the user agent +35
Selenium, Geckodriver __webdriver_evaluate, __selenium_evaluate, __fxdriver_evaluate, __driver_unwrapped +35
Cypress, TestCafe, Nightmare Framework globals (__cypress, window.Cypress, __testcafe, __nightmare) +35

The framework checks add +35 once, however many match.

Identifies browsers running without a visible UI (+25):

Signal Description
HeadlessChrome user agent Chrome’s headless mode
PhantomJS user agent PhantomJS
Firefox without mozInnerScreenX/Y Headless Firefox
Empty navigator.vendor with Headless in the user agent Other headless builds

The script matches the user agent against GPTBot, ClaudeBot, PerplexityBot, CCBot, OAI-SearchBot, anthropic-ai, Googlebot and Bingbot (+50). As explained above, these crawlers do not run JavaScript, so this only fires for a client that claims one of these names and executes the script.

The detection script tracks mouse movements, clicks, scrolls, and keystrokes to detect non-human patterns:

Signal Points What It Detects
Low mousemove count +25* Fewer than 5 mouse events (bots often skip mouse simulation)
Linear paths +20 Mouse moves in perfectly straight lines (humans curve)
Constant velocity +15 No speed variation (humans accelerate/decelerate)
Instant movements +15 More than 30% of mouse events arrive under 1 ms apart
Grid-aligned moves +10 Positions on exact coordinates (automation artifacts)

Mouse and click signals are skipped on mobile devices. *Counted only when other strong bot indicators are present.

Signal Points What It Detects
Instant clicks +30 No delay between mouse stop and click (humans have reaction time)
No pre-movement +20 Clicks without preceding mouse movement (teleporting cursor)
Ghost clicks +45 Clicks at positions the mouse never approached
Signal Points What It Detects
Constant scroll speed +15 Same speed throughout (humans vary). Skipped on mobile
Signal Points What It Detects
Synthetic mouse movements +40 Mouse events with isTrusted === false
Synthetic clicks +35 Click events with isTrusted === false (keyboard activation is excluded)

The script counts keystrokes but does not score typing rhythm.

Builds a fingerprint from browser characteristics:

  • Canvas rendering
  • WebGL renderer and parameters
  • Audio context
  • Navigator properties (platform, hardware concurrency, device memory, plugins)
  • Screen properties
  • Timezone/language
  • Storage availability
Signal Points What It Detects
Software renderer +30 SwiftShader, llvmpipe, softpipe or “software” in the renderer string (common in headless Chrome)
No unmasked renderer or vendor +25 GPU info hidden (real browsers expose this)
WebGL unavailable +20* No WebGL context
Low extension count +15* Fewer than 10 WebGL extensions

*Counted only when other strong bot indicators are present.

Signal Points What It Detects
AudioContext unavailable +15* API missing or blocked
Zero audio fingerprint +45 Mocked AudioContext returns zero (counted as both “zero” +20 and “mocked” +25)
Audio timeout +10* Fingerprint did not complete in time (not counted on mobile)

*Counted only when other strong bot indicators are present.

The detection script generates a unique hash from canvas rendering:

  1. Draws specific shapes and text
  2. Uses specific fonts and colors
  3. Generates hash from the rendered output
Signal Points What It Detects
Canvas unavailable +20 No 2D canvas context
Canvas blocked +15 Canvas readback returns empty or near-empty output

Chrome DevTools Protocol (CDP) is the automation protocol used by ChromeDriver, Puppeteer, Playwright, and other browser automation tools. These tools inject identifiable artifacts that are extremely difficult to hide.

The script looks for these artifacts:

Artifact What It Detects
cdc_* / $cdc_* properties on window or document ChromeDriver injects cdc_* prefixed variables
__cdp_binding__ CDP runtime binding artifacts
__chromium_protocol__ Chromium protocol handler markers

Any CDP artifact adds +40 to the client score, once. Selenium, Geckodriver and Puppeteer globals are counted under Automation Detection instead.

Why CDP detection is highly reliable:

  1. Protocol-level injection - These artifacts are injected by the automation framework itself, not the browser
  2. Hard to remove - Removing them requires patching the automation tool’s source code
  3. Specific to automation - Normal browsers do not have these properties
  4. Catches stealth attempts - Tools that try to hide navigator.webdriver often leave other CDP traces

Example detection:

// ChromeDriver leaves these artifacts:
window.cdc_adoQpoasnfa76pfcZLmcfl_Array // Random but always cdc_ prefixed
window.cdc_adoQpoasnfa76pfcZLmcfl_Promise // Multiple cdc_ properties

The detection script performs additional environment integrity checks that verify browser APIs behave correctly. Automation tools sometimes incorrectly mock or break these APIs.

The MessageChannel API enables communication between different browsing contexts. Some automation frameworks incorrectly implement or break this API.

Signal Points What It Detects
MessageChannel failure +10* Message not received within 100ms, or the API throws or is unavailable (counted once)

*Weak signal: Only counted when combined with other strong bot indicators. This prevents false positives from legitimate environments that restrict MessageChannel (some sandboxed iframes, older browsers).

Why this detection works:

  • Real browsers have full MessageChannel support
  • Automation frameworks sometimes mock MessageChannel incorrectly
  • The test is fast (100ms timeout) and non-blocking
  • Very low performance impact

The script also scores a few inconsistencies that stealth tools leave behind:

Signal Points What It Detects
Chrome object inconsistencies +10 each (max 30) A desktop Chrome user agent without the expected window.chrome object and related checks
Permission inconsistency +20 Permissions API answers that contradict each other
Iframe navigator.webdriver is true +40 Automation that patched the top window to hide its webdriver flag
Iframe without a chrome object +25 Chrome object missing or empty inside a fresh iframe
Other iframe mismatches +10 Weak on its own (privacy extensions cause it too)
AI browser extensions +15 / +10 / +8 / +5 each Claude, ChatGPT, Copilot, Gemini and Perplexity extensions (+15); other AI writing tools (+10); other AI signals (+8); modified MutationObserver (+5)
Wrapped fetch/XHR 0 Diagnostic observations; security and analytics tags legitimately wrap these APIs
Clicks from extension code +40 Clicks whose stack trace comes from an extension
High extension click ratio +25 More than 30% of clicks from extension code

The client score is capped at 100.


The detection script does not inject elements into your pages. It checks hidden form fields you add yourself: any input with an inline display: none or visibility: hidden style, or a class containing honeypot. If one of those fields has a value, the detection gets the honeypot flag and +60 client score, and the value is sent with it.

<!-- Add to your form: invisible to humans, filled in by form bots -->
<input type="text"
name="website_url"
class="honeypot"
style="display: none"
tabindex="-1"
autocomplete="off">
Behavior Result
Field is empty No honeypot signal
Field has value Honeypot flag, +60

Best for: Contact forms, signup forms, comment sections.

For hidden links that catch crawlers, create a decoy link and place it on your pages.


  1. Go to Detection Scripts
  2. Find your scanner in the list
  3. Click Copy Snippet (or the copy icon)
<script async src="https://cdn.webdecoy.com/bot-detection/v1/bot-detection.min.js"
data-aid="your-property-uuid"
data-sid="your-scanner-uuid">
</script>
Attribute Required Description
src Yes CDN URL for the script
data-aid Yes Your property UUID (the site this snippet is installed on)
data-sid Yes Your detection script UUID
data-endpoint No Custom ingest endpoint (default: https://ingest.webdecoy.com/api/v1/detect/public)

There is one script. The lite/pro tier split is gone, and so is the separate “pro” bundle.

Version URL
Minified https://cdn.webdecoy.com/bot-detection/v1/bot-detection.min.js
Source https://cdn.webdecoy.com/bot-detection/v1/bot-detection.js

The older /v1/pro/bot-detection-pro.min.js paths still serve the same bytes, so existing installs are not broken, but new snippets use the URLs above. If you have the old path hard-coded somewhere, it is safe to leave and better to update.

Add the snippet before the closing </head> tag in your shared layout:

<!DOCTYPE html>
<html>
<head>
<title>Your Site</title>
<!-- WebDecoy Detection Script -->
<script async src="https://cdn.webdecoy.com/bot-detection/v1/bot-detection.min.js"
data-aid="your-property-uuid"
data-sid="your-scanner-uuid">
</script>
</head>
<body>
<!-- Your content -->
</body>
</html>
  1. Create a new Custom HTML tag
  2. Paste the snippet
  3. Set trigger to All Pages
  4. Publish the container

Add to your theme’s footer.php:

<?php if (!is_admin()) : ?>
<script async src="https://cdn.webdecoy.com/bot-detection/v1/bot-detection.min.js"
data-aid="your-property-uuid"
data-sid="your-scanner-uuid">
</script>
<?php endif; ?>
// _app.js or layout.js
import Script from 'next/script';
export default function App({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
<Script
src="https://cdn.webdecoy.com/bot-detection/v1/bot-detection.min.js"
data-aid="your-property-uuid"
data-sid="your-scanner-uuid"
strategy="afterInteractive"
/>
</>
);
}
App.vue
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
mounted() {
const script = document.createElement('script');
script.src = 'https://cdn.webdecoy.com/bot-detection/v1/bot-detection.min.js';
script.setAttribute('data-aid', 'your-property-uuid');
script.setAttribute('data-sid', 'your-scanner-uuid');
script.async = true;
document.body.appendChild(script);
}
};
</script>

In the browser:

  1. Load your website in a browser
  2. Open Developer Tools (F12)
  3. Go to the Network tab
  4. Look for the bot-detection.min.js request
  5. Check the Network tab for a request to the ingest endpoint (/api/v1/detect/public) or the install heartbeat (api.webdecoy.com/api/public/install-heartbeat)

In the app, the Sensors page (sidebar, under Countermeasures) answers the question the browser cannot: whether detections are actually arriving. Your site’s card lists a Detection Script sensor with when it was last seen and its counts over 24h and 7d, and it distinguishes the states that look alike from the outside:

Row says Means
Reporting Detections are arriving
Nothing reported yet Installed, nothing has come in
Quiet It has reported before, but not recently

A Nothing reported yet on a busy site usually means the script is loading but every visitor is scoring under the reporting threshold, which is the normal state for a site with only human traffic. Remember that a detection script sees only clients that run JavaScript, so silence here is not silence overall. See the caution at the top of this page.


Go to Detection Scripts to see all scripts:

Column Description
Name Scanner identifier
Status Enabled toggle
Detections Detection count attributed to the script
Created Creation date
Actions Copy Snippet, Edit, Delete
  1. Find the scanner in the list
  2. Toggle the switch in the Status column
  1. Click the menu (three dots)
  2. Select Edit
  3. Change the Name or Reporting domain
  4. Click Save
  1. Click the menu (three dots)
  2. Select Delete
  3. Confirm deletion
  4. Scanner and snippet stop working immediately

  • ✅ Add hidden honeypot fields to your forms
  • ✅ Test on staging before production
  • ✅ Review detections for false positives
  • ✅ Combine with server-side detection
  • ❌ Block users based solely on scanner results
  • ❌ Install multiple scanners on the same page
  • ❌ Forget to update snippet when changing scanners

In addition to bot detection, the detection script automatically tracks visitors arriving from AI platforms like ChatGPT, Perplexity, Claude, and others.

Once per browser session, the script’s install heartbeat includes where the session came from: the referrer’s origin (only when it is another site) and the landing path with only the utm_source, ref and utm_medium parameters. WebDecoy classifies that against known AI platforms and counts the session. This runs independently of bot detection since these are human visitors.

ChatGPT, Perplexity, Google Gemini, Claude, DeepSeek, Microsoft Copilot, You.com, Phind, Kagi, Meta AI, and Grok.

  • Counted, not stored as detections: the session is counted against the AI platform; nothing else from it is kept
  • Once per session: only the session’s first page reports it
  • Independent of bot detection: does not interfere with threat scoring
  • Counts appear under AI Traffic in the sidebar

For full details, see AI Detections: LLM Referral Tracking.


The detection script collects data for detection purposes:

Data Type Collected Purpose
Browser properties ✅ Basic detection
Mouse coordinates ✅ Movement pattern analysis
Click positions ✅ Click behavior analysis
Scroll positions ✅ Scroll pattern analysis
Keystroke count ✅ Activity counting
Canvas fingerprint ✅ Rendering consistency
WebGL parameters ✅ Hardware fingerprinting
Audio fingerprint ✅ Audio context verification

Important:

  • Data is used solely for bot detection scoring
  • No actual keystrokes are captured (only a count)
  • Data is not used for user tracking or advertising
  • Consider your privacy policy when deploying

  1. Check the Sensors page first: it tells you whether anything has ever arrived from this script, which separates “not installed” from “installed and quiet”
  2. Check the browser console for [WebDecoy Pro] warnings (shown when data-aid or data-sid is missing)
  3. Verify the script loads (a 404 on the src is silent, because the tag is async)
  4. Verify data-aid is your property UUID and data-sid your scanner UUID
  5. Remember that a visitor must reach a client score of 20 (first phase) or 10 (behavioral phase) before a detection is sent
  6. Check network tab for requests to ingest endpoint
  1. Ensure behavioral phase has time to collect data (users need 5+ seconds on page)
  2. Check that users interact with page (mouse movement, scrolling)
  3. Review detection metadata to see which signals are triggering
  4. Consider if bots are leaving before Phase 2 completes

Scripts are served with permissive CORS headers. If you see CORS errors:

  1. Ensure you’re loading from cdn.webdecoy.com
  2. Check if a proxy or CDN is stripping headers
  3. Verify no browser extensions are blocking requests