LW Firewall
Lightweight WordPress firewall - rate-limits endpoints, blocks bots, bans repeat offenders, and adds security headers.
Overview
| Requires WordPress | 6.6+ |
| Requires PHP | 8.1+ |
| Tested up to | 6.7 |
| License | GPL-2.0-or-later |
| GitHub | lwplugins/lw-firewall |
Installation
composer require lwplugins/lw-firewallOr upload the lw-firewall folder to /wp-content/plugins/ and activate through the Plugins menu. The MU-plugin worker is installed automatically on activation.
Quick search
To find a setting, use the Quick search field at the top of the settings sidebar (or press Cmd/Ctrl+K) and type its name: the match opens on its tab, scrolled into view and highlighted.
Features
How It Works
LW Firewall installs an MU-plugin worker that intercepts requests before WordPress fully loads, protecting your server from bots, brute-force attacks, DDoS, and vulnerability scanners.
Processing order:
- IP Whitelist - whitelisted IPs skip all checks
- IP Blacklist - blacklisted IPs get 403 immediately
- Geo Blocking - block entire countries (Cloudflare header or CIDR lookup)
- Auto-Ban - previously banned IPs get 403
- 404 Flood - IPs with excessive 404s get 429
- Bot Blocking - User-Agent matching (all requests)
- Endpoint Detection - filter params, cron, xmlrpc, login, REST API
- Rate Limiting - per-IP counters with auto-ban escalation
Endpoint Protection
| Endpoint | Protection |
|---|---|
| WooCommerce filters | Rate limit + bot blocking |
wp-login.php | Brute-force rate limiting |
wp-cron.php | DDoS rate limiting |
xmlrpc.php | DDoS/brute-force rate limiting |
REST API (/wp-json/) | Rate limiting |
| 404 pages | Vulnerability scanner blocking |
WooCommerce filter protection sets a visitor cookie (lwfw_v, plus lwfw_c for a moment during the check). With LW Cookie 1.9.7 or newer, both are declared as strictly necessary automatically and are never blocked.
Geo Blocking
- Block visitors by country (Cloudflare or CIDR fallback)
- Enabled by default for CN, RU, IN, VN, ID, BD
- Apache-level geo blocking via .htaccess - blocks before PHP loads
- Configurable block action (403 Forbidden or redirect to homepage)
Bot Blocking
- User-Agent based blocking (20+ bad bots blocked by default)
- Applies to all requests, not just specific endpoints
IP Management
- IP whitelist and blacklist with CIDR range support
- Automatic server/localhost IP whitelisting
- Full IPv6 support
Auto-Ban
- Escalating protection for repeat offenders
- Per-IP violation counters
Security HTTP Headers
- X-Content-Type-Options
- X-Frame-Options
- And other configurable security headers
Storage Backends
| Backend | Description |
|---|---|
| APCu | Fastest - in-memory, per-process |
| Redis | Fast - shared across processes |
| File | Fallback - always works |
Auto-detection picks the best available backend.
Two-Factor Login
Optional and off by default. After the password, users confirm the sign-in with one of these:
- Authenticator app (TOTP) - any standard app; 10 one-time recovery codes are created when it is set up.
- Passkeys (WebAuthn) as a second factor, up to 10 per account.
- Passwordless passkey sign-in - a “Sign in with a passkey” button on the login page. A passkey replaces both the password and the second step.
Around the sign-in:
- Required roles with a grace period. Pick the roles that must use a second factor. During the grace period (7 days by default) they only get a reminder; afterwards they are taken to the setup page at login instead of being locked out. The forced setup is confirmed with a code sent to the account’s e-mail address.
- Remember this device for a number of days (0 = off, the default), with a button to forget all remembered devices.
- Notifications. The account owner gets an e-mail when a second factor, the recovery codes or the remembered devices change.
- Self-service on the profile page: set up or remove the authenticator app, add and remove passkeys, create new recovery codes. Sensitive steps ask for the password while the account has no second factor; once it has one, they ask for a second factor (authenticator code, passkey or recovery code).
- Two-Factor tab in the settings with usage per role, the options above and an optional switch that disables application passwords for users who have a second factor.
- Admin reset. An administrator can reset any user’s two-factor login from that user’s profile.
Two-factor login needs the PHP sodium and openssl extensions. Without them it stays off and an admin notice explains why.
I lost my phone
| Way | How |
|---|---|
| Recovery code | Choose “Use a recovery code” on the second step. Each code works once. |
| Another administrator | Use the reset button on your user profile. |
| WP-CLI | wp lw-firewall 2fa reset <user> |
| Emergency off switch | Add define( 'LW_FIREWALL_TWOFA_ENABLED', false ); to wp-config.php. Two-factor login switches off at once. |
Known interactions
- A captcha-type plugin that checks fields posted to the login form refuses passkey sign-in, because that request carries no password. Use the passkey only as a second factor there.
- Signing in on a host name that is not the site’s home/site URL host (for example a
wwwalias) needs thelw_firewall_passkey_allowed_originsfilter:
add_filter( 'lw_firewall_passkey_allowed_origins', function ( array $origins ): array {
$origins[] = 'https://www.example.com';
return $origins;
} );- Passkeys belong to the site’s domain. Moving the site to a new domain invalidates them; the authenticator app and the recovery codes keep working.
- Password logins outside the login page (XML-RPC apps, REST API login plugins, AJAX login forms of themes or page builders) are refused for accounts that use a second factor, and for accounts in a required role once their grace period ends: those requests cannot show the second step. Give integrations an application password instead, and keep Block Application Passwords off for those accounts.
- On multisite the two-factor settings are network-wide, because a sign-in is valid on every site of the network. A network administrator sets them in the main site’s Two-Factor tab; the other sites show them read-only. Network-activate the plugin so that every site asks for the second factor.
Additional Features
- Cloudflare-aware IP detection (CF-Connecting-IP with IP range validation)
- Import/Export settings as JSON
- Optional request logging with viewer
wp-config.phpconstant overrides
Settings
Configure under LW Plugins > Firewall. The settings page uses a tabbed interface with these tabs:
| Tab | Description |
|---|---|
| General | Firewall toggle, storage backend, rate limits |
| Protection | Endpoint protection toggles |
| Bot Blocking | User-Agent blocklist management |
| IP Rules | Whitelist and blacklist management |
| Geo Blocking | Country-based blocking |
| Security | HTTP security headers |
| Logs | Request log viewer |
| Import/Export | Transfer settings between sites |
| WooCommerce | Filter parameter rate limiting |
| Two-Factor | Two-factor login: required roles, grace period, remember device, passwordless passkey login, usage per role |
WP-CLI Commands
Status
wp lw-firewall statusShows firewall status overview: enabled, storage, rate limit, worker status, etc.
Configuration
# List all settings
wp lw-firewall config list
wp lw-firewall config list --format=json
# Set a value
wp lw-firewall config set rate_limit 50
wp lw-firewall config set enabled true
wp lw-firewall config set storage apcu
wp lw-firewall config set action 429
# Reset to defaults
wp lw-firewall config reset --yesBlocked Bots
# List blocked User-Agents
wp lw-firewall bots list
wp lw-firewall bots list --format=json
# Add a bot
wp lw-firewall bots add "newbot/1.0"
# Remove a bot
wp lw-firewall bots remove "newbot/1.0"Logs
# View recent logs
wp lw-firewall logs list
wp lw-firewall logs list --limit=50
wp lw-firewall logs list --format=json
# Clear all logs
wp lw-firewall logs clear --yesWorker Management
# Install/reinstall the MU-plugin worker
wp lw-firewall worker install
# Remove the worker
wp lw-firewall worker removeIP Management
# List whitelist or blacklist
wp lw-firewall ip list whitelist
wp lw-firewall ip list blacklist
wp lw-firewall ip list whitelist --format=json
# Add an IP or CIDR range
wp lw-firewall ip add whitelist 192.168.1.100
wp lw-firewall ip add blacklist 10.0.0.0/8
# Remove an IP or CIDR range
wp lw-firewall ip remove whitelist 192.168.1.100
wp lw-firewall ip remove blacklist 10.0.0.0/8Geo Blocking
# List blocked countries
wp lw-firewall geo list
wp lw-firewall geo list --format=json
# Add a country (ISO 3166-1 alpha-2 code)
wp lw-firewall geo add CN
wp lw-firewall geo add RU
# Remove a country
wp lw-firewall geo remove CN
# Update CIDR cache for all blocked countries
wp lw-firewall geo updateTwo-Factor Login
# Usage per role, or one user's factors
wp lw-firewall 2fa status
wp lw-firewall 2fa status alice
# Remove every factor, recovery code and remembered device of a user
wp lw-firewall 2fa reset alice --yesConfiguration via wp-config.php
Settings can be overridden via constants in wp-config.php:
define( 'LW_FIREWALL_ENABLED', true );
define( 'LW_FIREWALL_STORAGE', 'apcu' );
define( 'LW_FIREWALL_RATE_LIMIT', 50 );
define( 'LW_FIREWALL_RATE_WINDOW', 120 );
define( 'LW_FIREWALL_ACTION', '429' );
define( 'LW_FIREWALL_LOG_ENABLED', true );
// Emergency off switch for two-factor login
define( 'LW_FIREWALL_TWOFA_ENABLED', false );AI Abilities
When used with LW Site Manager, the following abilities are available for AI agents. All abilities are registered under the firewall category and require manage_options capability.
lw-firewall/get-options (readonly)
Returns all LW Firewall settings merged with defaults.
Input: none
Output:
{
"success": true,
"options": {
"enabled": true,
"rate_limit": 30,
"rate_window": 60,
"ip_blacklist": [],
"ip_whitelist": [],
"blocked_bots": ["gptbot", "claudebot"],
"geo_enabled": true,
"blocked_countries": ["CN", "RU"]
}
}lw-firewall/get-log (readonly)
Returns recent blocked request log entries. Requires log_enabled to be active in settings.
Input:
| Field | Type | Default | Description |
|---|---|---|---|
limit | integer | 25 | Number of entries to return (1-100) |
Output:
{
"success": true,
"entries": [
{ "ip": "1.2.3.4", "reason": "rate_limit", "ua": "...", "url": "/wp-login.php", "time": "2025-01-01 12:00:00" }
],
"total": 42
}lw-firewall/list-blocked (readonly)
Returns all IP addresses and CIDR ranges currently on the manual blacklist.
Input: none
Output:
{
"success": true,
"ips": ["1.2.3.4", "10.0.0.0/8"],
"total": 2
}lw-firewall/block-ip (write)
Adds an IP address or CIDR range to the blacklist.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
ip | string | yes | IP address (e.g. 1.2.3.4) or CIDR range (e.g. 10.0.0.0/8) |
Output:
{ "success": true, "message": "1.2.3.4 has been added to the blacklist." }Errors: 400 missing_ip, 400 invalid_ip
lw-firewall/unblock-ip (write)
Removes an IP address or CIDR range from the blacklist.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
ip | string | yes | IP address or CIDR range to remove |
Output:
{ "success": true, "message": "1.2.3.4 has been removed from the blacklist." }Errors: 400 missing_ip, 404 ip_not_found
Notes:
- The blacklist changes (
block-ip/unblock-ip) are persisted immediately.- Auto-banned IPs (set by the auto-ban system in storage) are separate from the manual blacklist and cannot be managed through these abilities.
- Log entries are only available when
log_enabledistruein the firewall settings.
FAQ
Does it work without WooCommerce?
Yes. WooCommerce filter protection is optional. The firewall also protects wp-login.php, wp-cron.php, xmlrpc.php, REST API, and 404 floods independently.
What storage backend should I use?
APCu is fastest (in-memory, per-process). Redis is fast and shared across processes. File-based is the fallback that always works. Auto-detection picks the best available.
Will it block legitimate users?
Rate limits are per-IP. Casual users will not trigger them. Only bots and attackers sending many requests in a short window get blocked. You can whitelist trusted IPs.
Does it support Cloudflare?
Yes. It automatically detects the real visitor IP via the CF-Connecting-IP header with Cloudflare IP range validation to prevent spoofing.
I lost my phone and cannot sign in. What now?
Use one of your recovery codes on the second step; each works once. If you have no code left, another administrator can reset your two-factor login from your user profile, or run wp lw-firewall 2fa reset <user>. If every administrator is locked out, add define( 'LW_FIREWALL_TWOFA_ENABLED', false ); to wp-config.php: two-factor login switches off at once and accounts sign in with their password again.
Passkey sign-in does not work on my site.
Three known cases: a captcha-type plugin that checks the login form fields refuses it (the request has no password); a host name other than the site address (for example a www alias) needs the lw_firewall_passkey_allowed_origins filter; and moving the site to a new domain invalidates existing passkeys. See Two-Factor Login above.
An app or plugin can no longer sign in with my password.
For an account with a second factor, and for an account in a required role once its grace period ends, password logins outside the login page are refused (XML-RPC apps, REST API login plugins, AJAX login forms). Create an application password on the user’s profile for the integration instead.
Does two-factor login work on multisite?
Yes. The settings are network-wide: a network administrator sets them on the main site, and the other sites show them read-only. Network-activate the plugin so that every site asks for the second factor.