=== Botfighter ===
Contributors: YOUR_WORDPRESS_ORG_USERNAME
Tags: bot protection, security, spam, scraper, firewall
Requires at least: 5.8
Tested up to: 6.7
Requires PHP: 7.4
Stable tag: 1.2.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Server-side bot blocking for WordPress — a 403 before content is served, no DNS change, no proxy.

== Description ==

Botfighter blocks confirmed bots server-side, before WordPress renders a response. Unlike JavaScript-only detection, this stops non-JS clients too (curl, Python scrapers, headless tools that never execute page scripts).

**What it does:**

* Checks the visitor's IP against your site's Botfighter blocklist (a network-effect list built from confirmed bad IPs across all Botfighter-protected sites, plus any IP confirmed bad on your own site specifically).
* Optionally verifies a signed cookie set by the Botfighter JavaScript snippet, for stricter per-visitor blocking.
* Blocks any request to a decoy/honeypot path configured in your Botfighter dashboard.
* Detects when your host runs a full-page cache in front of PHP (WP Engine, Kinsta, Pantheon, Flywheel, or any LiteSpeed Cache host) and warns you in wp-admin, since cached pages bypass this plugin entirely — a gap no PHP plugin can close from inside PHP.

**Requires a free Botfighter account** at [botfighter.dev](https://botfighter.dev) to get a Site ID and (optionally) an owner token.

This is a one-time, site-wide install — activate the plugin once and it protects every page automatically. It does not need to be added per-page or per-post.

== External services ==

This plugin connects to Botfighter's API (`api.botfighter.dev`), operated by Botfighter, to enforce blocking:

* **On every page load** (cached 5 minutes): a request to `https://api.botfighter.dev/v1/blocklist/{your-site-id}` to fetch the current list of blocked IP addresses for your site. No visitor data is sent — this is a read-only fetch of Botfighter's own blocklist data.
* **Once per day** (cached 24 hours), only if you enable cookie-based blocking mode: a request to `https://api.botfighter.dev/v1/cookie-config/{your-site-id}` (authenticated with your owner token) to fetch the signing key used to verify the Botfighter snippet's cookie.
* **On each page load**, a request to `https://api.botfighter.dev/v1/maze-config/{your-site-id}` to fetch your configured decoy/honeypot paths.

No visitor IP addresses, page content, or personal data are sent to Botfighter by this plugin itself (the separate, optional JavaScript snippet — installed independently — is what reports visit signals to your dashboard; see [botfighter.dev](https://botfighter.dev) for its own data handling).

Botfighter's terms of service and privacy policy: [botfighter.dev](https://botfighter.dev).

== Installation ==

1. Upload the plugin files to `/wp-content/plugins/botfighter`, or install directly through the WordPress plugins screen.
2. Activate the plugin through the **Plugins** screen in WordPress.
3. Go to **Settings → Botfighter**.
4. Enter your **Site ID** and **Owner Token** (from your [botfighter.dev](https://botfighter.dev) dashboard).
5. Choose a **Block mode**. For a normal public website, use **IP blocklist** (recommended) — it blocks confirmed-bot IPs and is safe for search-engine indexing. Only use a cookie-based mode (**IP + cookie** or **Cookie only**) for gated content that should not be indexed (see the FAQ below).

== Frequently Asked Questions ==

= Does this need to be added to every page or post? =

No. Activating the plugin protects your entire site immediately — there is nothing to add per-page.

= Will this block Google / hurt my SEO? =

Only if you choose a cookie-based mode. The signed cookie is issued only to visitors classified as human, so search-engine crawlers (Googlebot, Bingbot) never receive one and would be stopped at the "Checking your browser" page — which will deindex a public site. Use the default **IP blocklist** mode for anything you want in search results; it only blocks IPs already confirmed to be bots and never touches search crawlers. Reserve **IP + cookie** / **Cookie only** for gated content (member areas, staging) that should not be indexed anyway.

= I'm on a managed WordPress host and blocking doesn't seem to work on some pages =

Managed hosts that cache full pages (WP Engine, Kinsta, Pantheon, Flywheel) or run LiteSpeed Cache can serve a cached page without ever running this plugin's check. The plugin detects this and shows an admin warning with next steps. Cached pages need a cache-exclusion rule from your host, or an edge-level blocker (see [Edge Guard](https://docs.botfighter.dev/blocking) for Cloudflare/Vercel/Netlify sites) in front of your WordPress install.

= Does this replace the Botfighter JavaScript snippet? =

No — the snippet detects and reports bot signals to your dashboard (client-side, JS-only). This plugin adds real server-side blocking (stops non-JS scrapers too). Most sites use both.

== Changelog ==

= 1.2.0 =
* Fixed cookie verification: the HMAC key was being hex-decoded before use, which rejected every legitimate cookie — in Cookie/Both mode all verified visitors were denied.
* Visitors without a valid cookie now receive a "Checking your browser" page (with the Botfighter snippet) instead of a bare 403 — a bare 403 made it impossible for real first-time visitors to ever obtain the cookie. Non-JS scrapers still get no site content.
* Default block mode changed from Cookie to IP blocklist (safe for search-engine indexing). Added an SEO warning: cookie-based modes block search crawlers and should only be used on non-indexed content.

= 1.1.1 =
* Detect LiteSpeed Cache hosts (e.g. Hostinger) in the cache-warning notice, in addition to WP Engine/Kinsta/Pantheon/Flywheel.

= 1.1.0 =
* Added cache-host detection and admin warning for managed hosts that cache full pages.
* Block responses now sent with explicit no-store headers so a caching layer never serves a stale "Access denied" page to a real visitor.

= 1.0.0 =
* Initial release: IP blocklist blocking, signed-cookie blocking, decoy/honeypot path blocking.
