wordpress-honeypot
Realistic WordPress honeypot middleware for trapping scanners, bots, and AI agents.
Serves fake but believable WordPress responses — config files, debug logs, database dumps, login pages, phpMyAdmin — to waste the time of automated attackers and collect intelligence on their methods.
Features
- 70+ honeypot endpoints:
.env,wp-config.php,phpinfo,phpmyadmin,xmlrpc, REST API, debug logs, backup scripts, SSH keys, MongoDB credentials... - Runtime injection:
robots.txtandsitemap-0.xmlare enriched with decoy paths at request time — no file rewriting - 5 framework adapters: Express, Fastify, Hono, Koa, Node
http - Cloudflare Workers compatible: no
node:fs, all files embedded at build time - Parameterized via
SiteConfig: domain, DB credentials, server details — all configurable, auto-detected from request headers - Only intercepts known paths: routes defined after
app.use(honeypot)are never shadowed - Realistic headers:
X-Powered-By: PHP/7.4.33,Server: Apache/2.4.51 (Debian),X-Backend-Server: web-01 - MockupPaths: type-safe constants for all 1500+ embedded files
Install
npm install wordpress-honeypot
Quick Start
import express from "express";
import { expressMiddleware } from "wordpress-honeypot/express";
const app = express();
app.use(expressMiddleware({ domain: "example.com" }));
app.listen(8080);
Frameworks
| Framework | Import | Example |
|---|---|---|
| Express | wordpress-honeypot/express |
expressMiddleware(config) |
| Fastify | wordpress-honeypot/fastify |
fastifyPlugin(config) |
| Hono | wordpress-honeypot/hono |
honoMiddleware(config) |
| Koa | wordpress-honeypot/koa |
koaMiddleware(config) |
| Node http | wordpress-honeypot/node |
nodeHttpHandler(config) |
Express
import express from "express";
import { expressMiddleware } from "wordpress-honeypot/express";
const app = express();
// Auto-detect domain from Host header
app.use(expressMiddleware());
// Or with explicit config
app.use(expressMiddleware({
domain: "example.com",
siteName: "My Blog",
}));
// With emitter for logging
import { HoneypotEmitter } from "wordpress-honeypot";
const emitter = new HoneypotEmitter();
emitter.on("hit", (hit) => console.log(`[HONEYPOT] ${hit.path} from ${hit.ip}`));
app.use(expressMiddleware({ domain: "example.com" }, { emitter }));
app.listen(8080);
Fastify
import Fastify from "fastify";
import { fastifyPlugin } from "wordpress-honeypot/fastify";
const app = Fastify();
await app.register(fastifyPlugin, {
domain: "example.com",
siteName: "My Blog",
});
app.listen({ port: 8080 });
Hono
import { Hono } from "hono";
import { honoMiddleware } from "wordpress-honeypot/hono";
const app = new Hono();
// Works on Cloudflare Workers, Deno, Bun, Node
app.use("*", honoMiddleware({ domain: "example.com" }));
export default app;
Koa
import Koa from "koa";
import { koaMiddleware } from "wordpress-honeypot/koa";
const app = new Koa();
app.use(koaMiddleware({ domain: "example.com" }));
app.listen(8080);
Node http
import { createServer } from "node:http";
import { nodeHttpHandler } from "wordpress-honeypot/node";
const server = createServer((req, res) => {
// Returns true if honeypot handled the request
const handled = nodeHttpHandler({ domain: "example.com" })(req, res);
if (handled) return;
// Your real routes
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello, real world!");
});
server.listen(8080);
Configuration
SiteConfig controls the fake values injected into honeypot templates. It has no effect on your actual server, database, or infrastructure — it only determines what appears in the deceptive responses served to scanners.
import { expressMiddleware } from "wordpress-honeypot/express";
app.use(expressMiddleware({
domain: "example.com", // required — or auto-detected from Host header
siteName: "my-blog",
dbName: "wordpress",
dbUser: "wp_admin",
dbPassword: "change_me",
adminEmail: "[email protected]",
phpVersion: "7.4.33",
serverSoftware: "Apache/2.4.51 (Debian)",
serverName: "web-01",
vpsIp: "1.2.3.4",
sshPort: 22,
webroot: "/var/www/example.com",
themeName: "theme",
}));
All fields are optional except domain (auto-detected from Host / X-Forwarded-Host if omitted).
Note:
SiteConfigdoes not interact with your actual server or infrastructure. It only determines the values used in template replacements — the fake content served to scanners. Your real database, SSH keys, and config files are never touched.
SiteConfig Reference
| Field | Default | Description |
|---|---|---|
domain |
(required) | Domain name, auto-detected from request headers |
siteName |
domain |
WordPress site name |
dbName |
"wordpress" |
Database name |
dbUser |
"wp_user" |
Database username |
dbPassword |
"change_me" |
Database password |
adminEmail |
admin@${domain} |
Admin email address |
phpVersion |
"7.4.33" |
PHP version header |
serverSoftware |
"Apache/2.4.51 (Debian)" |
Server software header |
serverName |
"web-01" |
Backend server name |
vpsIp |
"0.0.0.0" |
VPS IP address |
sshPort |
22 |
SSH port |
webroot |
/var/www/${domain} |
Web root path |
themeName |
"theme" |
WordPress theme name |
Endpoints
The middleware intercepts these paths and returns realistic fake content:
Config Files
| Path | Content |
|---|---|
/.env |
Environment variables with DB credentials, API keys |
/.env.production |
Production environment config |
/.env.backup |
Backup environment config |
/wp-config.php |
WordPress config with DB credentials |
/wp-config.php.bak |
Backup of wp-config |
/wp-config-sample.php |
Sample WordPress config |
/.my.cnf |
MySQL client config with credentials |
/.htaccess |
Apache rewrite rules |
WordPress Core
| Path | Content |
|---|---|
/wp-login.php |
Realistic WordPress login page |
/wp-admin/ |
Redirects to wp-login.php |
/xmlrpc.php |
XML-RPC API response |
/wp-cron.php |
WordPress cron endpoint |
/wp-blog-header.php |
WordPress bootstrap |
/wp-includes/version.php |
WordPress version info |
/readme.html |
WordPress about page |
/license.txt |
WordPress license |
REST API
| Path | Content |
|---|---|
/wp-json/wp/v2/users/ |
User list with avatars |
/wp-json/wp/v2/posts/ |
Post list |
Server Info
| Path | Content |
|---|---|
/phpinfo.php |
PHP info page |
/server-status/ |
Apache mod_status |
/server-info/ |
Apache server info |
/actuator/ |
Spring Boot actuator |
/actuator/health |
Health check endpoint |
/api/health/ |
Custom health endpoint |
Sensitive Files
| Path | Content |
|---|---|
/todo.txt |
Developer todo list |
/notes.md |
Developer notes with server details |
/backup.sh |
Backup script with credentials |
/composer.json |
PHP dependencies |
/test.php |
Test script |
/license.txt |
License file |
Database & Backups
| Path | Content |
|---|---|
/wp-content/debug.log |
PHP debug log with errors |
/wp-content/uploads/*_backup.sql |
Database dump |
/wp-content/languages/fr_FR.po |
French translation file |
SSH & System
| Path | Content |
|---|---|
/.ssh/id_rsa |
SSH private key (fake) |
/.ssh/id_ecdsa |
ECDSA private key (fake) |
/.ssh/id_ed25519 |
Ed25519 private key (fake) |
/root/.bash_history |
Bash history with commands |
/root/.card_payment |
Credit card data (fake) |
/root/.msmtprc |
Mail client config |
/root/.ovh_config |
OVH hosting config |
/home/*/.bash_history |
User bash history |
Database Credentials
| Path | Content |
|---|---|
/mongo/.credentials |
MongoDB credentials |
/mongo/replica.conf |
MongoDB replica config |
Admin Tools
| Path | Content |
|---|---|
/phpmyadmin/ |
phpMyAdmin login page |
/phpmyadmin/index.php |
phpMyAdmin index |
Custom API Endpoints
| Path | Content |
|---|---|
/api/users/ |
User list with roles |
/api/auth/session.json |
Session error with leaked key |
/api/admin/ |
Admin forbidden response |
/api/internal/config.json |
Internal config with service accounts |
/api/internal/metrics |
Metrics endpoint |
Catchall
Unknown paths return a realistic WordPress 404 page.
See ALL_ENDPOINTS for the complete list.
Public API
Core Functions
import {
generateMockup, // Generate raw content for an endpoint
getResponse, // Generate full HTTP response (status, headers, body)
getPhpHeaders, // Get realistic PHP/Apache headers
detectDomain, // Auto-detect domain from request headers
ALL_ENDPOINTS, // List of all supported endpoints
} from "wordpress-honeypot";
Runtime Injection
import {
injectRobotsTxt, // Inject honeypot Disallow rules into robots.txt
injectSitemap, // Inject decoy URLs into sitemap-0.xml
} from "wordpress-honeypot";
MockupPaths
Type-safe constants for all embedded files:
import { loadWww, MockupPaths } from "wordpress-honeypot";
const content = loadWww(MockupPaths._wp_login_php, config);
How it works
- Request comes in for
/.env.production - Middleware checks if the path matches a known honeypot endpoint
- If yes → loads the corresponding file from
www/, appliesSiteConfigreplacements, returns it with realistic PHP headers - If no → calls
next()and lets your real routes handle it
Route Safety
The middleware uses classifySpecific() to check if a path is a known honeypot endpoint. Only known paths are intercepted — routes defined after app.use(honeypot) are never shadowed.
Runtime Injection
robots.txt and sitemap-0.xml are injected at request time:
- robots.txt:
Disallow:rules added for sensitive paths - sitemap-0.xml: Decoy
<url>entries added for honeypot endpoints
Embedded Files
All 1500+ files from www/ are embedded at build time as a Record<string, string> map. No node:fs required — works on Cloudflare Workers, Deno, and other edge runtimes.
Cloudflare Workers
Works out of the box on Cloudflare Workers — no node:fs required. All honeypot files are embedded at build time.
See worker-demo/ for a ready-to-deploy example using Hono:
import { Hono } from "hono";
import { honoMiddleware } from "wordpress-honeypot/hono";
const app = new Hono();
app.use("*", honoMiddleware({ domain: "example.com" }));
export default app;
Static Deployment (GitHub Pages + Cloudflare)
No server needed. Deploy the raw www/ directory to GitHub Pages, put Cloudflare in front, and use Transform Rules to fake a PHP/Apache stack.
How it works
- Copy the contents of
www/to your GitHub Pages root (or a subfolder) - Enable Cloudflare proxy on your domain (orange cloud)
- Create a Transform Rule to inject fake PHP headers and strip GitHub fingerprints
- Scanners see a realistic WordPress/Apache server — not GitHub Pages
Step 1: Deploy www/ to GitHub Pages
# Clone the repo (or copy the www/ directory)
git clone https://github.com/fox3000foxy/wordpress-honeypot.git
cd wordpress-honeypot
# Copy www/ contents to your GitHub Pages repo
cp -r www/* /path/to/your-github-pages-repo/
Or use a GitHub Action to deploy www/ directly:
# .github/workflows/deploy.yml
name: Deploy honeypot
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: www
- uses: actions/deploy-pages@v4
Step 2: Enable Cloudflare Proxy
- Add your domain to Cloudflare (free plan works)
- Point DNS to
username.github.iowith orange cloud enabled (proxied) - This hides the real GitHub Pages IP from scanners
Step 3: Transform Rules (Headers)
Cloudflare Transform Rules inject fake PHP headers and strip GitHub/Fastly fingerprints.
Headers to Add
| Header | Value | Effect |
|---|---|---|
X-Powered-By |
PHP/7.4.33 |
Fake PHP signature (typical WordPress) |
X-Backend-Server |
web-01 |
Suggests an internal Apache server |
X-Cache |
MISS |
Cloudflare cache status (consistent with a VPS) |
Server |
Apache/2.4.51 (Debian) |
Fake Apache server banner |
Headers to Remove
| Header | Why |
|---|---|
X-GitHub-Request-Id |
Reveals GitHub Pages origin |
x-github-edge-region |
Reveals GitHub edge location |
X-Fastly-Request-ID |
Reveals Fastly CDN (GitHub's CDN) |
X-Served-By |
Reveals Fastly backend |
X-Timer |
Reveals Fastly timing |
X-Cache-Hits |
Reveals Fastly cache layer |
Cloudflare Dashboard Setup
Go to Rules → Transform Rules → Modify Response Header:
Rule 1 — Add fake PHP headers:
| Action | Header Name | Value |
|---|---|---|
| Set static | X-Powered-By |
PHP/7.4.33 |
| Set static | X-Backend-Server |
web-01 |
| Set static | X-Cache |
MISS |
| Set static | Server |
Apache/2.4.51 (Debian) |
Rule 2 — Remove GitHub/Fastly headers:
| Action | Header Name |
|---|---|
| Remove | X-GitHub-Request-Id |
| Remove | x-github-edge-region |
| Remove | X-Fastly-Request-ID |
| Remove | X-Served-By |
| Remove | X-Timer |
| Remove | X-Cache-Hits |
Terraform / API (Alternative)
# Cloudflare Transform Rules via Terraform
resource "cloudflare_ruleset" "honeypot_headers" {
zone_id = var.cloudflare_zone_id
name = "Honeypot Headers"
kind = "zone"
phase = "http_response_headers_transform"
rules {
action = "rewrite"
action_parameters {
headers {
name = "X-Powered-By"
operation = "set"
value = "PHP/7.4.33"
}
headers {
name = "X-Backend-Server"
operation = "set"
value = "web-01"
}
headers {
name = "X-Cache"
operation = "set"
value = "MISS"
}
headers {
name = "Server"
operation = "set"
value = "Apache/2.4.51 (Debian)"
}
# Remove GitHub/Fastly fingerprints
headers { name = "X-GitHub-Request-Id" operation = "remove" }
headers { name = "x-github-edge-region" operation = "remove" }
headers { name = "X-Fastly-Request-ID" operation = "remove" }
headers { name = "X-Served-By" operation = "remove" }
headers { name = "X-Timer" operation = "remove" }
headers { name = "X-Cache-Hits" operation = "remove" }
}
expression = "true"
description = "Inject fake PHP headers and strip GitHub fingerprints"
enabled = true
}
}
Step 4: Verify
# Check that GitHub headers are gone and PHP headers are present
curl -I https://yourdomain.com/
# Should NOT contain:
# X-GitHub-Request-Id
# X-Fastly-Request-ID
# X-Served-By
# Should contain:
# X-Powered-By: PHP/7.4.33
# Server: Apache/2.4.51 (Debian)
# X-Backend-Server: web-01
# X-Cache: MISS
# Check honeypot endpoints
curl -s https://yourdomain.com/.env.production
curl -s https://yourdomain.com/wp-config.php
curl -s https://yourdomain.com/robots.txt
Limitations
- No runtime injection on static hosting:
robots.txtandsitemap-0.xmlare served as-is fromwww/(noinjectRobotsTxt/injectSitemap). Edit them manually if needed. - No dynamic responses: endpoints like
/api/users/serve static JSON files, not generated content. - GitHub Pages serves 404.html for missing paths — configure a custom 404 page in
www/404.html(the included one is a WordPress-style 404). - Cloudflare free plan has a 100k requests/day limit — sufficient for most honeypot use cases.
Development
Build
npm run build:files # Generate src/files.generated.ts from www/
npm run build # Build files + compile TypeScript
Test
npm test # Run all tests
bun test --coverage # Run with coverage
Lint
npx biome lint src/ # Lint source files
npx biome format src/ # Format source files
License
MIT