ThreatHive.net Firewall for WordPress

A WordPress plugin that downloads the ThreatHive blocklist and blocks listed visitors before your site renders a page. It works on single sites and multisite, behind Cloudflare and other proxies, and can run ahead of page caches.

Plugin version
1.0.1
WordPress
5.8 or newer
PHP
7.4 or newer
Feed used
Full blocklist

Download threathive-firewall.zip

Before you start

  • The web server user must be able to write to wp-content/. The plugin stores its data in wp-content/threathive-firewall/.
  • The server needs outbound HTTPS to threathive.net, and to www.cloudflare.com if the site is behind Cloudflare.
  • WP-Cron must run, or a system cron must call wp-cron.php (see step 6).

Setup

  1. Install and activate the plugin In the WordPress admin, go to Plugins › Add New › Upload Plugin, choose threathive-firewall.zip, then click Install Now and Activate. The first blocklist download starts within a few seconds.
    • Multisite: install it from Network Admin › Plugins and click Network Activate. One configuration protects the whole network, so activating on a single subsite is refused. Settings are under Network Admin › ThreatHive Firewall.
    • Manual install: unzip so the files end up in wp-content/plugins/threathive-firewall/, then activate it from the Plugins screen.
  2. Check the status page Open ThreatHive Firewall in the admin menu and check three things:
    • The status badge in the top right says Protecting. If it says Waiting for first download, wait a minute and reload, or click Update now.
    • IPs on blocklist matches the size of the full blocklist.
    • Your IP shows your real public address and not listed. If it says listed, add your address to the Allowlist and save before doing anything else, or you can lock yourself out.
    The ThreatHive.net Firewall dashboard widget shows the same status and the 20 most blocked addresses to administrators.
  3. Confirm the plugin sees real visitor addresses Leave Client IP source on Automatic and read the Connection row on the status page:
    Via Cloudflare, using CF-Connecting-IPThe site is behind Cloudflare. Nothing to do; Cloudflare's IP ranges are built in and refreshed weekly.
    Via trusted proxy, using forwarded headerRequests come through a proxy you trust, such as Cloudflare Tunnel, a local nginx or Varnish. Click Re-test proxy once; it should report Proxy test passed.
    Requests arrive with proxy headers, but that address is not trustedA separate proxy or load balancer sits in front of WordPress. If it is your own server-side proxy, click Trust <address> as my proxy. The plugin tests it first. Don't trust addresses that belong to an office network, VPN or browser proxy.
    Direct (REMOTE_ADDR)No proxy in front of the site. Nothing to do.
    If the proxy test fails, configure the proxy to set the forwarded header itself:
    # nginx
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
    # HAProxy
    option forwardfor
  4. Optional: block before the page cache The firewall loads as the first plugin, but a page cache (WP Super Cache, W3 Total Cache, LiteSpeed Cache and similar) serves cached pages before any plugin runs. Early loading from wp-config.php blocks those requests too.
    1. Copy the lines shown under Early loading on the status page. Always copy them from there, because they contain your site's real paths. They look like this:
      if ( ! defined( 'THFW_DATA_DIR' ) ) {
          define( 'THFW_DATA_DIR', '/var/www/.../wp-content/threathive-firewall' );
      }
      if ( is_file( '/var/www/.../wp-content/plugins/threathive-firewall/early.php' ) ) {
          require_once '/var/www/.../wp-content/plugins/threathive-firewall/early.php';
      }
    2. Paste them into wp-config.php between /* Add any custom values between this line and the "stop editing" line. */ and /* That's all, stop editing! Happy publishing. */.
    3. Restart PHP so OPcache picks up the change, for example sudo systemctl restart php8.3-fpm (use your PHP version), or restart the web server.
    4. Reload the status page. Early loading should say Active via wp-config.php. If it says Not active, follow the reason shown; usually PHP needs another restart.
  5. Nginx only: protect the data directory Nginx ignores .htaccess. The plugin checks daily whether its data directory is reachable from the web and shows a red notice if it is. Add this to the site's server { } block, then run sudo nginx -t && sudo systemctl reload nginx:
    location ^~ /wp-content/threathive-firewall/ {
        deny all;
        return 404;
    }
    Apache and IIS are protected automatically.
  6. Recommended on quiet sites: use a real cron WP-Cron only runs when someone visits, so list updates can be late on low-traffic sites. Disable it in wp-config.php with define( 'DISABLE_WP_CRON', true ); and add a cron job for the web server user:
    */5 * * * * curl -fsS -o /dev/null https://example.com/wp-cron.php?doing_wp_cron
    
    # or with WP-CLI
    */5 * * * * cd /var/www/example && wp cron event run --due-now --quiet

Settings

ModeEnforce (block with HTTP 403), Monitor (log only) or Off. On busy sites, run Monitor for a day first.
Auto-updateFrom every 15 minutes (recommended) to daily.
Client IP sourceAutomatic (recommended). See step 3.
Trusted proxiesYour own proxies and load balancers, one per line.
Also trust this server's own addressOn by default. Needed for Cloudflare Tunnel and local reverse proxies.
AllowlistAddresses or CIDR ranges that are never blocked, one per line. # comments are allowed.
Keep IP-level logs1 to 90 days, 30 by default. Older logs containing IP addresses are deleted; daily totals without addresses are kept.
Block page linkOff by default. When on, the block page links to ThreatHive.net for delisting.

Upgrading

Upload the new zip under Plugins › Add New › Upload Plugin and choose Replace current with uploaded. Settings, statistics and the downloaded list are kept. Open the firewall page once afterwards, and click Re-test proxy if you use a proxy or tunnel.

Coming from 1.0.0: the old runtime.php is replaced by runtime.json.php automatically. If you use early loading, replace the wp-config.php lines with the new ones from the status page and restart PHP.

Troubleshooting

You locked yourself outAdd define( 'THFW_DISABLE', true ); to wp-config.php, or rename wp-content/plugins/threathive-firewall over SFTP. Log in, add your address to the Allowlist, save, then undo the change. THFW_DISABLE also turns off early loading.
Not protecting: data directory is not writableMake wp-content writable by the web server user, or set a writable path with define( 'THFW_DATA_DIR', '/path/to/writable/dir/threathive-firewall' );.
Could not reach threathive.netCheck outbound HTTPS from the server with curl -I https://threathive.net/hiveblocklist.txt. The last good list stays active in the meantime.
Download rejected, use Force updateThe new list shrank by more than 80% or grew more than tenfold. Check the feed, then click Force update if it is correct. The previous list stays active until then.
Protecting, list outdatedNo successful update for 3 days. Check WP-Cron (step 6) and the Last result row.
Your IP shows a proxy or server addressSee step 3.

Uninstalling

If you enabled early loading, remove its lines from wp-config.php first. Then deactivate and delete the plugin (from Network Admin on multisite). Deleting removes all settings, statistics, logs and the wp-content/threathive-firewall directory.

Last reviewed 4 November 2026. Questions or corrections: [email protected].