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
Install in about 2 minutes
Section titled “Install in about 2 minutes”- 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.
- Paste the generated line into the shared layout for your site, just before
</head>. - 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.
What Is a Detection Script?
Section titled “What Is a Detection Script?”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.
How Detection Scripts Work
Section titled “How Detection Scripts Work”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 CalculatedWhy 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.
What Detection Scripts Detect
Section titled “What Detection Scripts Detect”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 |
What it does not catch
Section titled “What it does not catch”| 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 |
Creating a Detection Script
Section titled “Creating a Detection Script”Step-by-Step Guide
Section titled “Step-by-Step Guide”-
Navigate to Detection Scripts
- Click Detection Scripts in the sidebar, under Countermeasures
-
Click “Create Scanner”
- The Create Detection Script dialog opens
-
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” -
Click “Create Scanner”
- Detection script is created with a unique ID
-
Install the Snippet
- Copy the provided JavaScript snippet
- Add it to your website (see Installing the Snippet)
What the script does (fixed behavior)
Section titled “What the script does (fixed behavior)”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.)
Detection Signals
Section titled “Detection Signals”Automation Detection
Section titled “Automation Detection”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.
Headless Browser Detection
Section titled “Headless Browser Detection”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 |
AI Crawler Detection
Section titled “AI Crawler Detection”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.
Behavioral Analysis
Section titled “Behavioral Analysis”The detection script tracks mouse movements, clicks, scrolls, and keystrokes to detect non-human patterns:
Mouse Movement Signals
Section titled “Mouse Movement Signals”| 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.
Click Signals
Section titled “Click Signals”| 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 |
Scroll Signals
Section titled “Scroll Signals”| Signal | Points | What It Detects |
|---|---|---|
| Constant scroll speed | +15 | Same speed throughout (humans vary). Skipped on mobile |
Synthetic Events
Section titled “Synthetic Events”| 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.
Browser Fingerprinting
Section titled “Browser Fingerprinting”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
WebGL Deep Fingerprinting
Section titled “WebGL Deep Fingerprinting”| 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.
Audio Fingerprinting
Section titled “Audio Fingerprinting”| 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.
Canvas Fingerprinting
Section titled “Canvas Fingerprinting”The detection script generates a unique hash from canvas rendering:
- Draws specific shapes and text
- Uses specific fonts and colors
- 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 |
CDP Artifact Detection
Section titled “CDP Artifact Detection”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:
- Protocol-level injection - These artifacts are injected by the automation framework itself, not the browser
- Hard to remove - Removing them requires patching the automation tool’s source code
- Specific to automation - Normal browsers do not have these properties
- Catches stealth attempts - Tools that try to hide
navigator.webdriveroften leave other CDP traces
Example detection:
// ChromeDriver leaves these artifacts:window.cdc_adoQpoasnfa76pfcZLmcfl_Array // Random but always cdc_ prefixedwindow.cdc_adoQpoasnfa76pfcZLmcfl_Promise // Multiple cdc_ propertiesEnvironment Integrity Checks
Section titled “Environment Integrity Checks”The detection script performs additional environment integrity checks that verify browser APIs behave correctly. Automation tools sometimes incorrectly mock or break these APIs.
MessageChannel Communication Test
Section titled “MessageChannel Communication Test”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
Other Consistency Checks
Section titled “Other Consistency Checks”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.
Form Honeypot Fields
Section titled “Form Honeypot Fields”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.
Installing the Detection Script Snippet
Section titled “Installing the Detection Script Snippet”Getting Your Snippet
Section titled “Getting Your Snippet”- Go to Detection Scripts
- Find your scanner in the list
- Click Copy Snippet (or the copy icon)
Snippet Format
Section titled “Snippet Format”<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>Snippet Attributes
Section titled “Snippet Attributes”| 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) |
CDN URLs
Section titled “CDN URLs”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.
Installation Methods
Section titled “Installation Methods”Method 1: Direct HTML
Section titled “Method 1: Direct HTML”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>Method 2: Google Tag Manager
Section titled “Method 2: Google Tag Manager”- Create a new Custom HTML tag
- Paste the snippet
- Set trigger to All Pages
- Publish the container
Method 3: WordPress (Manual)
Section titled “Method 3: WordPress (Manual)”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; ?>Method 4: React/Next.js
Section titled “Method 4: React/Next.js”// _app.js or layout.jsimport 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" /> </> );}Method 5: Vue.js
Section titled “Method 5: Vue.js”<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>Verifying Installation
Section titled “Verifying Installation”In the browser:
- Load your website in a browser
- Open Developer Tools (F12)
- Go to the Network tab
- Look for the
bot-detection.min.jsrequest - 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.
Managing Detection Scripts
Section titled “Managing Detection Scripts”Viewing Script List
Section titled “Viewing Script List”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 |
Enabling/Disabling a Scanner
Section titled “Enabling/Disabling a Scanner”- Find the scanner in the list
- Toggle the switch in the Status column
Editing a Scanner
Section titled “Editing a Scanner”- Click the menu (three dots)
- Select Edit
- Change the Name or Reporting domain
- Click Save
Deleting a Scanner
Section titled “Deleting a Scanner”- Click the menu (three dots)
- Select Delete
- Confirm deletion
- Scanner and snippet stop working immediately
Best Practices
Section titled “Best Practices”- ✅ Add hidden honeypot fields to your forms
- ✅ Test on staging before production
- ✅ Review detections for false positives
- ✅ Combine with server-side detection
Don’ts
Section titled “Don’ts”- ❌ Block users based solely on scanner results
- ❌ Install multiple scanners on the same page
- ❌ Forget to update snippet when changing scanners
LLM Referral Tracking
Section titled “LLM Referral Tracking”In addition to bot detection, the detection script automatically tracks visitors arriving from AI platforms like ChatGPT, Perplexity, Claude, and others.
How It Works
Section titled “How It Works”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.
Tracked Platforms
Section titled “Tracked Platforms”ChatGPT, Perplexity, Google Gemini, Claude, DeepSeek, Microsoft Copilot, You.com, Phind, Kagi, Meta AI, and Grok.
Key Points
Section titled “Key Points”- 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.
Privacy Considerations
Section titled “Privacy Considerations”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
Troubleshooting
Section titled “Troubleshooting”Detection not sending
Section titled “Detection not sending”- Check the Sensors page first: it tells you whether anything has ever arrived from this script, which separates “not installed” from “installed and quiet”
- Check the browser console for
[WebDecoy Pro]warnings (shown whendata-aidordata-sidis missing) - Verify the script loads (a 404 on the
srcis silent, because the tag isasync) - Verify
data-aidis your property UUID anddata-sidyour scanner UUID - Remember that a visitor must reach a client score of 20 (first phase) or 10 (behavioral phase) before a detection is sent
- Check network tab for requests to ingest endpoint
Low detection rate
Section titled “Low detection rate”- Ensure behavioral phase has time to collect data (users need 5+ seconds on page)
- Check that users interact with page (mouse movement, scrolling)
- Review detection metadata to see which signals are triggering
- Consider if bots are leaving before Phase 2 completes
CORS errors
Section titled “CORS errors”Scripts are served with permissive CORS headers. If you see CORS errors:
- Ensure you’re loading from
cdn.webdecoy.com - Check if a proxy or CDN is stripping headers
- Verify no browser extensions are blocking requests
Next Steps
Section titled “Next Steps”- Integrations - Connect to Cloudflare, Slack, webhooks
- Threat Scoring - Understand how scores are calculated
- Decoy Links - Set up server-side detection