Decoy Links & Honeypot URLs
Set up your first decoy
Section titled “Set up your first decoy”Create one trap, make a controlled request, and inspect the resulting detection before choosing a response. You can start with the shared decoy domain without changing your site’s DNS.
Start free · Follow the first-decoy setup
Already have an account? Sign in, then follow the steps below. A controlled test proves that the trap reports; it does not demonstrate a real attack or guarantee blocking on your own domain.
What Are Decoy Links?
Section titled “What Are Decoy Links?”A decoy link (also called a honeypot link) is a URL outside your normal visitor journey, placed to reveal unexpected access. A hit provides evidence to investigate: crawlers, security scanners, link previews and your own tests can all reach it. Inspect the context before treating it as an attack.
For use cases and product capabilities, see the Decoy Links product overview.
How Decoy Links Work
Section titled “How Decoy Links Work”Your Website├── /index.html ← Visible to users├── /about.html ← Visible to users├── /contact.html ← Visible to users└── /admin-backup.zip ← DECOY (hidden, only bots find it) │ ▼ Bot accesses decoy │ ▼ Detection created │ ▼ Alert/Block actionWhy Attackers Trigger Decoys
Section titled “Why Attackers Trigger Decoys”- Automated scanners crawl every link they find
- Vulnerability tools probe common paths like
/admin,/backup - Scrapers follow all links, including hidden ones
- Reconnaissance maps your entire site structure
The Deception Advantage
Section titled “The Deception Advantage”| Traditional Security | Deception Security |
|---|---|
| Tries to block known attacks | Detects unknown attackers |
| Relies on signatures | Catches by behavior |
| Must separate good traffic from bad | Legitimate visitors never see the link, so a hit is a strong signal |
| Attackers can evade | Attackers don’t know what’s a trap |
Creating Your First Decoy Link
Section titled “Creating Your First Decoy Link”Step-by-Step Guide
Section titled “Step-by-Step Guide”-
Navigate to Decoys
- Click Decoys in the sidebar
-
Click “Create Decoy”
- The Create New Decoy dialog opens, with Honeypot Link selected
-
Configure the Decoy
Field Description Example Name Internal name for identification “Admin Backup Trap” Hosting Use the WebDecoy shared domain (default) or a custom domain you’ve verified Shared domain Path URL path for the decoy /admin/backup.zipAction on Detection What happens when accessed; begin with Log while testing Log -
Click “Create Decoy”
- Your decoy is created and active immediately
-
Verify one controlled request
- Keep the action on Log and open the generated decoy URL once yourself.
- Open Detections and match the record by the decoy and request time.
- This is your own setup check. Do not count it as a real attack or assume it proves blocking works.
-
Deploy the Decoy
- Add a hidden link to your website pointing to the decoy URL
- See Deploying Decoy Links for deployment methods
Full Decoy URL
Section titled “Full Decoy URL”The URL depends on which hosting mode you picked:
Shared domain (default): WebDecoy generates a unique subdomain for each decoy.
https://{short_code}.d.webdecoy.com{path}Example: https://abc12345.d.webdecoy.com/admin/backup.zip
A shared-domain decoy answers on any path under its subdomain, so every URL on abc12345.d.webdecoy.com reaches the same decoy.
Custom domain (optional): Uses the domain you’ve added under Settings → Domains.
https://{your-domain}{path}Example: https://decoy.yoursite.com/admin/backup.zip
Both modes work the same for detection, scoring, and response actions, with one difference: clearance cookies are first-party, so they never reach the shared domain. The Clearance column on the Decoys list shows Can revoke for own-domain hosting (revocation is possible when that exact host receives a valid clearance cookie) and Detection only for shared-domain decoys (the bot is recorded but cannot be denied through its clearance).
Reading decoy activity
Section titled “Reading decoy activity”The Decoys list separates evidence about a trap from the action configured for it:
| Field | What it tells you |
|---|---|
| Recorded accesses | Retained request detections, excluding reserved tests and owner self-trips |
| Tests / Owner | Separate retained test-trigger and authenticated owner-visit records |
| Link delivery: Unknown | No acknowledged delivery tally is available for this decoy |
| Link delivery: At least N | Authenticated serving-point acknowledgments in retained UTC event-day buckets, with coverage and freshness |
| Rate unavailable | Delivery acknowledgments and access records do not provide compatible complete accounting for a percentage |
| Configured action | The chosen response action, rather than proof that enforcement ran |
| Outcomes not attributed | Confirmed enforcement outcomes are not currently attributed to this decoy in this summary |
A recorded zero means no ordinary access records are currently retained. It does not tell you whether the link was installed, delivered, inspected, ignored or effective. A dash means counts were unavailable or the backend did not report them. Unknown delivery and enforcement counts stay unknown, including when recorded accesses are zero. Review Enforcement for the site’s actual enforcement evidence; capability and configured action are separate facts.
These are records, not unique visitors or complete lifetime request totals. Each stored detection ID counts once; a retried trap request may make a separate record. Storage admission, sampling, suppression and retention can omit or remove requests, so counts are a measured lower bound of recorded requests. Pending records/late arrival can change a later read. A cached page containing a trap does not create a delivery measurement, and a cached trap response that never reaches ingest cannot create an access record. Organization-wide decoys show their records across sites and retain the all sites label.
The legacy API field clicks_count and click-limit behavior retain their existing
meaning: non-test records, including owner visits. Where owner visits exist,
the list also shows Limit count to explain why that number differs from
ordinary accesses. Tests remain outside the click limit, and test/owner visits
remain exempt from billing, actor correlation and automatic enforcement.
Pausing, expiry and first-party revocation behavior are unchanged.
API measurement fields
Section titled “API measurement fields”List/detail decoy responses add an activity object. This is an example of a
no-hit decoy with a successful record-count query:
{ "activity": { "exposure_status": "unknown", "trap_containing_responses": null, "access_counts_status": "available", "recorded_trap_accesses": 0, "recorded_test_accesses": 0, "recorded_owner_accesses": 0, "count_basis": "retained_detection_records", "enforcement_status": "not_attributed", "confirmed_enforcement_outcomes": null, "access_rate": null }}A failed count query returns access_counts_status: "unavailable" with null
record counts, rather than inventing a zero. Create/update responses report
unavailable counts until a list/detail read supplies them. These read-only fields
cannot be set by adding exposure numbers to decoy metadata.
Authenticated delivery receipts
Section titled “Authenticated delivery receipts”A controlled server/edge response writer can report a trap-containing response to
POST https://ingest.webdecoy.com/api/v1/decoy-deliveries. Create a private API key
with Report Decoy Delivery (decoy-delivery:write) and select one registered
property. Keep it on the server. The property must belong to that organization,
and each generic decoy must be explicitly assigned to it. Organization-wide
links and Shopify’s separate trap slugs need an explicit association/mapping;
public site keys and browser beacons cannot authenticate this measurement.
The reference Node response writer
records one event per decoy when a successful HTML response finishes, even when
that link occurs several times. It creates a fresh response UUID at the serving
point, including for a cached body. Cache hits require instrumentation at the
cache-serving writer; origin_responses_only reports cannot measure them.
A receipt means the writer offered a response, not that a visitor received every
byte, inspected the link, followed it or was blocked.
For example, a no-hit decoy with three acknowledged responses can report:
{ "exposure_status": "measured_lower_bound", "trap_containing_responses": 3, "delivery_coverage": ["origin_responses_only", "cache_serving_point"], "delivery_window_start": "2026-09-14T00:00:00Z", "delivery_window_end": "2026-10-14T00:00:00Z", "delivery_last_acknowledged_at": "2026-10-13T08:00:00Z", "delivery_property_rejected_attempts_utc_day": 0, "recorded_trap_accesses": 0, "access_rate": null, "confirmed_enforcement_outcomes": null}The dashboard shows At least 3, its reported origin/cache coverage, the UTC
window start and latest acknowledgment. Reporter IDs are included in the API as
delivery_reporter_ids. The window includes the current and previous 29 UTC
event-day buckets; the end is exclusive. Late receipts can update earlier
buckets. A tolerated future-dated next-day bucket appears on that UTC day.
Batches contain at most 100 events/32 KiB. Initial limits admit 60 new receipt pairs/property/minute and 10,000/property/UTC day across all reporter keys and replicas. Retrying the same organization/property/decoy/response identity adds nothing. Quota rejection is tallied as site receipt attempts rejected today; it is not a count of unique lost responses or a per-decoy count. Rejected retries can increment that tally again. Suppressed attempts cannot create unbounded receipt or reporter identities. Malformed, foreign/unassociated and stale batches are rejected atomically; no unverified body claim selects a tenant.
Exact dedupe IDs last seven days; aggregate buckets retain 30 UTC days. Cleanup uses indexed bounded deletes and may lag during an outage; reads exclude expired buckets. The producer’s bounded queue separately exposes outages, backoff, stale/overflow/shutdown discards. Unreported, sampled, lost or uninstrumented responses stay outside this acknowledged lower bound. No universal total is extrapolated.
An empty table remains Unknown, not a measured zero: there is no active heartbeat observation window. Access rates stay null because retained access records do not establish matching window, identity, cache/sampling completeness or retention. Delivery receipts do not supply an enforcement ledger link or change pause/expiry/click-limit/revocation behavior. Owned-site trace/dashboard reconciliation and budget evaluation remain tracked in #1585.
Trigger Actions
Section titled “Trigger Actions”When someone accesses a decoy, WebDecoy can respond in different ways.
Available Actions
Section titled “Available Actions”| Action | Behavior | Best For |
|---|---|---|
| Log | Record the access, return 200 OK | Monitoring, intelligence gathering |
| Block | Record and return 403 Forbidden | Active defense, deterrence |
| Poison | Return fake/misleading data | Wasting attacker time |
| Redirect | Redirect to another URL | Directing to legal notices |
Log Action
Section titled “Log Action”The default action. Records the detection and returns a normal-looking response.
Response:
- HTTP 200 OK
{"status": "ok"}
Use when:
- You want to monitor without tipping off attackers
- Gathering intelligence on attack patterns
- Running decoys in observation mode
Block Action
Section titled “Block Action”Records the detection and returns an access denied response.
Response:
- HTTP 403 Forbidden
{"error": "access forbidden"}
Use when:
- You want to actively deter attackers
- Combined with integration blocking (Cloudflare, WAF)
- Sending a clear “you’re caught” message
Poison Action
Section titled “Poison Action”Returns fake data designed to waste attacker time or mislead them.
Response:
- HTTP 200 OK
- The JSON you enter as Poison Content. Without it, a generic
{"status": "success", "data": [], ...}body
Examples:
- Fake database records with invalid data
- Honeypot credentials you can watch for elsewhere
Use when:
- You want to actively mislead attackers
- Wasting attacker resources
- Creating uncertainty about what’s real
Redirect Action
Section titled “Redirect Action”Redirects the request to another URL.
Response:
- HTTP 302 Redirect
- Redirects to the Redirect URL you configured (without one, it returns 200 with
{"status": "ok"})
Use when:
- Directing to a legal warning page
- Sending to a law enforcement reporting portal
- Redirecting to a CAPTCHA challenge
Advanced Decoy Options
Section titled “Advanced Decoy Options”Click Limit and Expiry Date
Section titled “Click Limit and Expiry Date”The create dialog has optional Click Limit and Expiry Date fields. When either is reached, the decoy stops answering exactly as a paused one does: requests to it return 404 and create no detection. The Decoys list shows it as Limit reached or Expired.
- Click Limit (1 to 1,000,000) counts the same clicks as the list’s click column: every detection recorded for the decoy except test-trigger requests. The count is refreshed about every 10 seconds, so a decoy under heavy traffic can record a few more clicks than its limit before it stops.
- Expiry Date takes effect at the start of the chosen day, in your browser’s time zone. It must be in the future.
To keep a stopped decoy catching bots, Resume it from its menu. Resuming removes the limit that stopped it: the Expiry Date of an expired decoy, the Click Limit of one that reached it. You are asked to confirm first. A limit that has not been reached is kept, and pausing never changes either limit.
Description
Section titled “Description”Add internal notes about the decoy:
Description: "Placed in robots.txt disallow section to catch botsthat ignore robots.txt rules. Monitoring campaign started 2025-01."Use for:
- Documenting the decoy’s purpose
- Tracking which campaign it belongs to
- Notes for team members
Poison Content
Section titled “Poison Content”When using the “Poison” action, specify what fake data to return:
{ "users": [ ]}Tips:
- Make it look realistic
- Include obvious honeypot markers for tracking
- Don’t include real data
Deploying Decoy Links
Section titled “Deploying Decoy Links”Decoys only work if attackers can find them. Here are methods to deploy decoy links.
Method 1: Hidden HTML Links
Section titled “Method 1: Hidden HTML Links”Add an invisible link to your website:
<!-- Hidden decoy link - invisible to users, visible to bots --><a href="https://decoy.yoursite.com/admin/backup.zip" style="display:none; visibility:hidden; position:absolute; left:-9999px;"> Admin Backup</a>Where to place:
- Footer of every page
- Inside a hidden
<div> - In the
<head>section as a<link>tag
Method 2: robots.txt (Reverse Psychology)
Section titled “Method 2: robots.txt (Reverse Psychology)”Add decoys to robots.txt with “Disallow” - malicious bots often target these paths:
User-agent: *Disallow: /admin/Disallow: /backup/Disallow: /admin/backup.zip # This is actually a decoy!Why this works:
- Legitimate bots respect robots.txt
- Malicious bots specifically target disallowed paths
- Creates a perfect trap
Method 3: HTML Comments
Section titled “Method 3: HTML Comments”Embed decoy URLs in HTML comments:
<!-- TODO: Remove before production Backup location: https://decoy.yoursite.com/db_backup_2024.sql-->Why this works:
- Attackers often search source code for sensitive paths
- Comments look like developer mistakes
- Very convincing to automated scanners
Method 4: JavaScript Variables
Section titled “Method 4: JavaScript Variables”Hide decoy URLs in JavaScript:
// Configuration - do not modifyvar config = { apiEndpoint: "/api/v1", adminPath: "https://decoy.yoursite.com/admin/debug", // Decoy! backupUrl: "https://decoy.yoursite.com/backup.tar.gz" // Decoy!};Method 5: Sitemap Honeypot
Section titled “Method 5: Sitemap Honeypot”Create a fake sitemap with decoy URLs:
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>https://decoy.yoursite.com/admin/config.php</loc> </url> <url> <loc>https://decoy.yoursite.com/backup/database.sql</loc> </url></urlset>Recommended Decoy Paths
Section titled “Recommended Decoy Paths”These paths are commonly targeted by attackers:
| Category | Example Paths |
|---|---|
| Admin | /admin, /administrator, /wp-admin, /admin.php |
| Backup | /backup.zip, /backup.sql, /db_backup.tar.gz |
| Config | /config.php, /settings.json, /.env, /wp-config.php |
| Debug | /debug, /phpinfo.php, /test.php, /info.php |
| API | /api/admin, /api/debug, /graphql/admin |
| Files | /passwords.txt, /users.csv, /credentials.json |
Viewing and Managing Decoys
Section titled “Viewing and Managing Decoys”Decoys List Page
Section titled “Decoys List Page”Navigate to Decoys to see all your decoy links:
| Column | Description |
|---|---|
| Name | Your internal name for the decoy (tagged all sites if it is shared by every site in the organization) |
| Status | Active or Inactive |
| Clearance | Can revoke or Detection only (see above) |
| Action | Log, Block, Poison, or Redirect |
| Detections | Number of detections recorded |
| Created | Creation date |
| Actions | Menu with Pause/Resume, Copy URL and Delete |
Filtering and Sorting
Section titled “Filtering and Sorting”Filter by Status:
- All
- Active
- Inactive
Sort by:
- Created (Newest)
- Detections (Most)
- Active Status
Searching Decoys
Section titled “Searching Decoys”Use the Search short code box to find a decoy by its short code.
Copying Decoy URL
Section titled “Copying Decoy URL”- Find the decoy in the list
- Click the menu (three dots)
- Select Copy URL
- The full URL is copied to your clipboard
Pausing a Decoy
Section titled “Pausing a Decoy”To deactivate or reactivate a decoy:
- Find the decoy in the list
- Click the menu (three dots)
- Select Pause (or Resume)
A paused decoy no longer answers: requests to it return 404 and create no detection.
An expired or limit-reached decoy shows Resume instead of Pause. See Click Limit and Expiry Date for what resuming it changes.
Deleting a Decoy
Section titled “Deleting a Decoy”- Click the menu (three dots)
- Select Delete
- Confirm the deletion
- The decoy is permanently removed
Note: Deleting a decoy keeps the detections it already recorded. They stay in your detection history, no longer linked to a decoy.
Decoy Best Practices
Section titled “Decoy Best Practices”- ✅ Use realistic-looking paths
- ✅ Deploy multiple decoys across your site
- ✅ Include decoys in robots.txt (as disallowed)
- ✅ Rotate decoys periodically
- ✅ Monitor detection patterns
Don’ts
Section titled “Don’ts”- ❌ Use obviously fake names like “honeypot.html”
- ❌ Place decoys where legitimate users might click
- ❌ Forget to deploy the link on your actual site
- ❌ Use the same decoy across all properties
- ❌ Ignore the detection data
Next Steps
Section titled “Next Steps”Learn about more advanced honeypots:
- Endpoints (API Honeypots) - Create fake API endpoints for detecting attacks