User Guide
How to install, configure, and use Login Delay Shield on your WordPress site.
Getting Started
Login Delay Shield is available on WordPress.org. It requires zero configuration — activate and it works immediately:
- In your WordPress admin, go to Plugins → Add New and search for “Login Delay Shield”.
- Click Install Now, then Activate.
- That’s it. Failed login attempts are now delayed by 1 second automatically.
For fine-tuning, navigate to Settings → Login Delay Shield. The settings page opens with the Security Setup Wizard and a protection summary showing how many features are enabled, followed by collapsible sections for each feature area.
Security Setup Wizard
The wizard sits at the top of the settings page and lets you apply a protection profile in one step. Each profile sets the delay, progressive delay, lockout, email alert, and authentication endpoint options to sensible values. Every individual control stays editable afterwards, so a profile is a starting point, not a lock-in.
| Profile | Best for |
|---|---|
| Conservative | Core throttling with gentler thresholds for low-friction sites. |
| Balanced | Recommended defaults for most WordPress sites. |
| Aggressive | Stricter lockouts and XML-RPC authentication blocking for sites under frequent attack. Counts lockouts by IP rather than IP + username, which closes a password-spray gap where one IP could try many usernames without locking out. |
A badge shows the active profile. Once you change any setting by hand, the badge reads Custom. Applying a profile does not touch your log retention setting, so switching profiles never deletes existing log history.
Delay Settings
The delay is applied only to failed login attempts — successful logins are always instant.
Fixed Delay
Set a fixed number of seconds (0–10) added after every failed login attempt. The default is 1 second. Even a 1-second delay is barely noticeable to legitimate users but makes brute-force attacks impractical (1 second × 10,000 attempts = nearly 3 hours).
Random Delay
Instead of a fixed value, use a random delay within a configurable range (e.g., 1–5 seconds). This prevents attackers from using timing analysis to detect the exact delay value.
| Setting | Default | Range |
|---|---|---|
| Fixed delay | 1 second | 0–10 seconds |
| Random delay (min) | 1 second | 1–10 seconds |
| Random delay (max) | 5 seconds | 1–10 seconds |
Progressive Delay
When enabled, the delay increases with each consecutive failed attempt from the same IP address. The formula adds the increment value for each failure until the maximum is reached.
For example, with a 1-second base delay, 1-second increment, and 30-second max:
| Attempt | Additional delay | Total delay |
|---|---|---|
| 1st failure | +1s | 1s |
| 2nd failure | +2s | 2s |
| 3rd failure | +3s | 3s |
| 10th failure | +10s | 10s |
| 30th+ failure | +30s (capped) | 30s |
Delays reset to zero after a successful login or after 1 hour of no failed attempts from that IP.
| Setting | Default | Range |
|---|---|---|
| Increment per attempt | 1 second | 1–10 seconds |
| Maximum delay | 30 seconds | 5–60 seconds |
IP Lockout
After a configurable number of failed attempts, the IP address (or IP + username pair) is temporarily blocked from logging in. During lockout, the login form shows a countdown message indicating when the user can try again.
| Setting | Default | Range |
|---|---|---|
| Failed attempts before lockout | 10 | 1–100 |
| Lockout duration | 60 minutes | 1–1,440 minutes (24h) |
| Tracking strategy | IP only | IP only / IP + username |
| Trust proxy headers | Off | On / Off |
Trust proxy headers — Enable this only if your server is behind a reverse proxy or CDN (Cloudflare, Sucuri, nginx, AWS ELB). When off, the plugin uses REMOTE_ADDR exclusively, which prevents IP spoofing. When on, it reads the real visitor IP from forwarding headers — CF-Connecting-IP (accepted only when the request actually comes from a Cloudflare IP range, so it cannot be spoofed), X-Sucuri-ClientIP, X-Real-IP, and X-Forwarded-For — and validates each value as an IP before using it.
Getting this wrong is the most painful misconfiguration the plugin can cause, so the settings page runs a proxy configuration health check: it warns when your site looks like it is behind a proxy but trust is off (every visitor shares the CDN’s IP, so one attacker can lock out everyone), and when trust is on but no proxy is in front (anyone can spoof their IP).
See Lockout Strategy below for details on IP only vs. IP + username tracking.
IP Whitelist
Add trusted IP addresses that bypass all delays and lockouts. Useful for office networks, VPN endpoints, or development servers.
Supported formats (one per line):
- Single IPv4 —
192.168.1.100 - Single IPv6 —
2001:db8::1 - IPv4 CIDR range —
192.168.1.0/24(covers 192.168.1.0–255) - IPv6 CIDR range —
2001:db8::/32
Whitelisted IPs bypass all security measures: delays, progressive delay, lockout, and email tracking.
Email Notifications
Get email alerts when failed login attempts cross a threshold. The plugin tracks failures per IP address and notifies you when the count is reached.
| Setting | Default | Range |
|---|---|---|
| Failed attempts before notification | 5 | 1–100 |
| Notification email address | Site admin email | Any valid email |
| Email cooldown | 5 minutes | 0–60 minutes |
The email includes the IP address, username attempted, number of failed attempts, and timestamp. The cooldown prevents inbox flooding during distributed attacks. During the cooldown period, only one email is sent site-wide, regardless of how many IPs trigger the threshold.
Set cooldown to 0 to disable rate-limiting (one email per threshold event).
XML-RPC Protection
xmlrpc.php is a common brute-force target. There are two levels of XML-RPC protection:
- Apply delays — XML-RPC authentication failures receive the same delay and lockout treatment as wp-login.
- Block entirely — All XML-RPC authentication is blocked immediately, returning an error to the caller.
If you use the WordPress mobile app, Jetpack, or remote publishing tools, use “apply delays” mode. If you don’t use any of these, blocking XML-RPC removes the attack surface entirely.
REST & App Password Protection
Beyond wp-login and XML-RPC, WordPress has two more authentication surfaces: the REST API and application passwords.
REST API Protection
When enabled, failed REST API authentication attempts receive the same delay, progressive delay, and lockout treatment as wp-login. This covers any third-party integration or headless frontend that authenticates via the REST API.
Application Password Protection
WordPress 5.6 introduced application passwords for authenticating external apps via HTTP Basic Auth. When this toggle is enabled, failed application-password attempts are tracked and subject to delays and lockout.
| Setting | Default | Description |
|---|---|---|
| REST API protection | Off | Apply delays and lockout to REST API authentication failures |
| Application password protection | Off | Apply delays and lockout to application-password authentication failures |
Both are off by default. Turn them on if your site authenticates through the REST API or application passwords and you want the same throttling applied there. Leave them off if another plugin already rate-limits these methods, or if a specific integration is being falsely throttled.
Password Reset Protection
The password reset form is another way attackers probe for valid accounts. When this option is on, password reset submissions get the same delay, lockout, and whitelist handling as login attempts, and each one is recorded in the log with the source password-reset.
Two things keep it from getting in the way of normal use:
- Isolated counters — Reset throttling uses its own counters and lockouts, so abuse of the reset form cannot lock a user out of the normal login screen.
- No information leak — Messages stay generic, so the form never reveals whether a username or email address exists. Password reset attempts also do not trigger the failed-login email alerts.
Reset-specific throttling keys are cleared by the same recovery tools (the Unlock Current IP button and the WP-CLI commands) used for login lockouts.
Distributed Attack Detection
Per-IP lockout cannot see a botnet: if 50 different IP addresses each try a few passwords against the same username, no single IP ever reaches the lockout threshold. Distributed attack detection watches the other axis — how many distinct IP addresses target one username within a time window — and raises an alert when the count crosses a threshold. This is the credential-stuffing pattern.
It is on by default and alert-only — it never blocks anyone. Enforcement stays per-IP by design, so legitimate users are never caught by it. When a distributed attack is detected, the plugin:
- writes an entry to the security audit trail;
- shows a warning on the WordPress dashboard;
- sends an email naming the targeted username and a few sample IPs — only when email notifications are enabled.
| Setting | Default | Range |
|---|---|---|
| Distributed attack detection | On | On / Off |
| Distinct IPs before alert | 5 | 2–100 |
| Detection window | 15 minutes | 5–60 minutes |
After an alert fires for a username, repeat alerts for that same username are suppressed for an hour so a sustained attack does not flood your inbox or audit log.
Login Log
All failed login attempts are logged to the database with the timestamp, username attempted, IP address, and source. The source indicates where the attempt came from: wp-login, xmlrpc, rest, application-password, or password-reset. Logs are used by the dashboard widget and can be filtered and exported.
| Setting | Default | Range |
|---|---|---|
| Log retention | 30 days | 0–365 days |
Old entries are automatically cleaned up by a daily cron job. Set retention to 0 to keep logs indefinitely. Cleanup runs in batches of 1,000 rows to avoid database locks on large tables.
fail2ban Logging
If your server runs fail2ban, Login Delay Shield can write failed-login and lockout events to a log file that fail2ban watches, so the firewall can ban offending IPs at the network level. Enable it under Settings → Login Delay Shield → Login Log.
Leave the log path empty and the plugin writes to a plugin-owned directory outside the WordPress uploads tree (login-delay-shield-fail2ban/login-delay-shield-fail2ban.log), and drops in .htaccess and index.html files so the log is not web-accessible. Custom paths are restricted to that protected directory by default. If you point logging at a path the plugin will not accept, logging is disabled rather than silently writing somewhere unexpected.
Each line carries an ISO-8601 timestamp and a stable prefix:
2026-05-04T12:00:00+00:00 Login Delay Shield: failed login source=wp-login ip=203.0.113.10 username=admin
A matching fail2ban filter regex:
failregex = Login Delay Shield: (?:failed login|lockout) .* ip=<HOST>
If lockout-event logging is on, an attempt that triggers a lockout can produce both a failed login line and a lockout line, so set your jail’s maxretry accordingly. The log rotates to a single .log.1 backup once it reaches 5 MB, so it cannot grow without bound on servers without external log rotation. Under a brute-force burst, lines are buffered and written once per request rather than once per attempt, which cuts disk I/O.
You don’t have to hand-write the fail2ban rules. The settings page has a one-click download that generates the filter and jail configuration matching your current log path and line format, ready to drop into filter.d and jail.local.
Custom Login URL
Hide your WordPress login page by replacing /wp-login.php with a custom URL slug (e.g., /my-login). This reduces automated bot traffic targeting the default login URL.
| Setting | Default | Description |
|---|---|---|
| Enable custom login URL | Off | Replace wp-login.php with a custom slug |
| Login slug | my-login | Lowercase letters, numbers, and hyphens only |
When enabled, login, logout, lost password, and password reset all route through the custom URL. Visiting /wp-login.php directly returns a 404. Reserved slugs (wp-admin, login, wp-json, wp-content, wp-includes, xmlrpc, feed, robots, sitemap, etc.) are automatically rejected.
Important: After enabling or changing the slug, visit Settings → Permalinks and click Save Changes to flush rewrite rules. Bookmark the new login URL so you don’t lose access.
Two safeguards keep this feature from locking you out:
- Self-check — when you enable the feature or change the slug, the plugin verifies the new URL actually responds. If it would return a 404, the feature is automatically disabled and
/wp-login.phpkeeps working, instead of stranding everyone behind a dead login page. If the check is inconclusive (some hosts block loopback requests), the feature stays on with a warning to verify the URL yourself first. - Recovery email — the new login URL is emailed to the site admin as a reminder. Disable it with the
wldelay_send_custom_login_emailfilter (see the developer reference).
If you ever get stuck behind the custom URL, define WLDELAY_DISABLE_CUSTOM_LOGIN in wp-config.php to restore standard /wp-login.php access.
Emergency Recovery URL
If you lock yourself out and have no admin access and no way to run WP-CLI, the Emergency Recovery URL is a way back in. It is a secret link you save ahead of time; opening it lets you clear the login lockout for your own IP address. It is off by default — turn it on under Settings → Login Delay Shield before you need it.
When you enable it (or click Regenerate), the plugin shows the URL once, offers it as a .txt download, and emails it to the site admin address. Save it somewhere safe and off this site — a password manager or a note on another device.
- It does not log you in. Opening the URL shows a confirmation page; clicking confirm clears the lockout for your current IP, then you sign in normally. It never disables the rest of the plugin.
- Treat it like a password. Only a hash of the token is stored, never the URL itself, so it cannot be recovered if lost — regenerate to get a new one (which instantly invalidates the old one).
- Abuse-resistant. Wrong tokens are rate-limited per IP, and the confirm step is a form submission so email and antivirus link-scanners can’t trigger it by accident. Every generate and use is recorded in the audit log.
- Rotation reminder. After 90 days the settings page reminds you to regenerate the token.
Security Health Score
The settings page shows a Security Health Score from 0 to 100 that reflects how much protection you have switched on. Alongside the score it lists a next recommended step, so you can see at a glance which protections (lockout, XML-RPC, password reset, and so on) are still worth enabling for your site.
After a plugin update, a dismissible What’s New notice highlights the latest additions. The score and notice are informational and do not change how protection behaves.
The settings page also flags contradictory settings — an enabled whitelist with no IPs in it, a fail2ban log path the server can’t write to, or an alert threshold that can never be reached — so you can fix them. These warnings never block you from saving.
Login Feedback
When lockout is enabled, the login screen shows two extra messages:
- Remaining attempts — After a failed login, the error message includes how many attempts remain before lockout (e.g., “Login failed. 3 attempts remaining before temporary lockout.”).
- Lockout countdown — When locked out, the message shows when the lockout expires (e.g., “Too many failed login attempts. Please try again in 2 minutes.”).
Messages are kept generic to avoid leaking information about whether a username exists.
Trend Analytics
The settings page includes a 7-day trends panel that gives you a quick overview of recent attack activity:
- Daily totals — Failed login attempts per day for the last 7 days
- Top IPs — The IP addresses with the most failed attempts
- Top usernames — The most targeted usernames
- Top target pairs — The most common IP + username combinations, for faster incident triage
Trends are computed from the login log database table, so they reflect the same data as the log filters and CSV export. If log retention is set to fewer than 7 days, the trends panel will show data only for available days.
Dashboard Widget
A widget on the WordPress dashboard shows the 10 most recent failed login attempts, including:
- Time of the attempt (relative, e.g., “5 minutes ago”)
- Username attempted
- IP address
- Source (wp-login, XML-RPC, REST API, application-password, or password-reset)
The widget data is cached briefly to minimize database queries. A link at the bottom takes you to the settings page.
Log Filters & Export
The settings page includes tools for investigating and exporting your failed login log.
Filtering
Filter log entries by any combination of:
- Source — wp-login, xmlrpc, rest, application-password, or password-reset
- IP address — show attempts from a specific IP
- Username — show attempts targeting a specific username
- Date range — from/to dates to narrow results to a time window
Filters apply to both the log view and CSV export. Clear all filters to return to the full log.
CSV Export
Click “Export CSV” to download the log as a CSV file. Each row contains the source, IP address, username, and timestamp. If filters are active, only matching entries are exported.
The export streams in batches so it works reliably even with very large logs (100,000+ entries) without running into memory limits. Cell values are sanitized to prevent spreadsheet formula injection.
Lockout Strategy
Choose how failed attempts are tracked for lockout and progressive delay:
IP Only (default)
One counter per IP address. All failed attempts from the same IP count together, regardless of which username was tried. Simple and effective for most sites.
IP + Username
Separate counters for each IP + username pair. If someone tries “admin” 10 times and “editor” 10 times from the same IP, each username has its own counter. This reduces false positives on shared networks (offices, mobile carriers, coworking spaces) where multiple legitimate users share an IP.
Recommendation: Use “IP + username” if your site has users on shared networks. Use “IP only” for single-user sites or when you want the strictest protection.
Lockout Recovery
If you or a legitimate user gets locked out, Login Delay Shield provides several ways to recover without deactivating the plugin. If you have admin access, use the unlock button; from the server, use WP-CLI; with neither, the pre-configured Emergency Recovery URL or Safe Mode get you back in.
Admin Unlock Button
On the settings page, the IP Lockout section includes an “Unlock Current IP” button. Clicking it immediately removes the lockout and resets the failure counter for your current IP address. This is useful when testing lockout settings or recovering from accidental lockouts.
WP-CLI Commands
If you cannot access the admin, use WP-CLI from the server command line:
# Unlock a specific IP address
wp login-delay-shield unlock-ip 203.0.113.42
# Clear all lockouts site-wide
wp login-delay-shield flush-lockouts
See WP-CLI below for full command details.
Safe Mode (emergency kill switch)
If you are completely locked out and cannot use the admin or WP-CLI, add this line to wp-config.php:
define( 'WLDELAY_SAFE_MODE', true );
Safe mode disables every delay, lockout, and tracking path — the plugin behaves as if every IP were whitelisted — so you can log back in. A persistent admin notice shows while it is active so you don’t leave your site unprotected. Remove the line once you have recovered access.
2FA Health Check
The settings page checks whether a two-factor authentication plugin is active. If one is found, the notice says which one. If not, it recommends installing one.
These 2FA plugins are recognized by default:
- Two Factor
- WP 2FA
- miniOrange 2-Factor Authentication
- Google Authenticator
If you use a different 2FA plugin, developers can register it through the wldelay_2fa_providers filter — see the developer reference for details.
This check is informational only. Login Delay Shield works independently of any 2FA plugin.
Object Cache Notice
When no persistent object cache is active (Redis, Memcached, etc.), the settings page shows a notice about it. Failure counters and lockout state live in WordPress transients, which fall back to the database without an object cache. On sites with heavy login traffic, that means extra database queries on every failed attempt.
Most managed WordPress hosts already include object caching. If you manage your own server, see the Performance section for recommended plugins.
WP-CLI
Login Delay Shield registers two WP-CLI commands for managing lockouts from the command line.
wp login-delay-shield unlock-ip <ip>
Removes the lockout and failure counter for a specific IP address. Validates the IP format before processing.
$ wp login-delay-shield unlock-ip 203.0.113.42
Success: Removed 2 entries for IP 203.0.113.42.
wp login-delay-shield flush-lockouts
Clears all lockouts and failure counters site-wide. Useful after a mass brute-force event or when resetting the plugin state.
$ wp login-delay-shield flush-lockouts
Success: Flushed 14 lockout entries.
Performance
Login Delay Shield uses WordPress transients to track failed attempts and lockout state. By default, transients are stored in the database.
For busy sites or sites under frequent attack, adding a persistent object cache (Redis, Memcached) cuts database load:
- Transient reads/writes go to memory instead of the database
- Reduces database queries from ~6 per failed attempt to ~2
- Most managed WordPress hosts (WP Engine, Kinsta, Flywheel) include object caching by default
Popular object cache plugins: Redis Object Cache, W3 Total Cache, LiteSpeed Cache.
Languages
Login Delay Shield is translated into 18 languages. The plugin automatically uses your site’s language setting.
- Arabic (العربية)
- Chinese Simplified (简体中文)
- Czech (Čeština)
- Dutch (Nederlands)
- French (Français)
- German (Deutsch)
- Indonesian (Bahasa Indonesia)
- Italian (Italiano)
- Japanese (日本語)
- Korean (한국어)
- Polish (Polski)
- Portuguese – Brazil (Português do Brasil)
- Russian (Русский)
- Spanish (Español)
- Swedish (Svenska)
- Thai (ไทย)
- Turkish (Türkçe)
- Vietnamese (Tiếng Việt)
Want to help translate? Visit translate.wordpress.org.
FAQ
What versions of WordPress and PHP are supported?
Login Delay Shield supports WordPress 3.5.1+ and PHP 7.4+. It has been tested up to WordPress 6.9 and works with PHP 8.x.
Will Login Delay Shield conflict with other security plugins?
Login Delay Shield uses standard WordPress hooks for login authentication. It is compatible with most security plugins. If another plugin also adds login delays or lockouts, you may want to disable one to avoid double-delays.
What happens if I lock myself out?
Use the “Unlock Current IP” button on the settings page, or run wp login-delay-shield unlock-ip <your-ip> via WP-CLI. With no admin and no server access, open your pre-saved Emergency Recovery URL, or define WLDELAY_SAFE_MODE in wp-config.php. You can also wait for the lockout to expire (default: 60 minutes), or whitelist your IP in advance. As a last resort, deactivate the plugin by renaming its folder in wp-content/plugins/ via FTP/SSH.
Does the delay affect successful logins?
No. Delays are applied only to failed login attempts. Successful logins are always instant.
Should I block XML-RPC?
If you don’t use the WordPress mobile app, Jetpack, or remote publishing tools (like Windows Live Writer), blocking XML-RPC authentication removes a common attack vector. Otherwise, use “apply delays” mode for protection without breaking functionality.
Is the admin interface accessible?
Yes. Login Delay Shield follows WCAG 2.1 Level AA guidelines. All settings are keyboard navigable, screen reader compatible, and include proper ARIA attributes. Collapsible sections toggle with Enter or Space keys, and tooltips appear on both hover and keyboard focus.
Where can I see failed login attempts?
A dashboard widget shows the 10 most recent failed login attempts, including the time, username attempted, IP address, and source. On the settings page you can filter the full log by source, IP, username, or date range, and export the results as a CSV file.
What are protection profiles?
Protection profiles are presets in the Security Setup Wizard. Applying Conservative, Balanced, or Aggressive sets the delay, progressive delay, lockout, email alert, and authentication endpoint options in one step, while leaving every individual control editable.
Should I protect password reset requests?
For most sites, yes. Attackers use the reset form to probe for accounts. Password reset protection applies the same delay and lockout behaviour as login, logs the source as password-reset, and keeps messages generic so the form does not reveal whether a username or email exists.
What is distributed attack detection, and does it block users?
It watches how many different IP addresses target the same username in a short window — the credential-stuffing pattern that per-IP lockout can’t catch. It is on by default and alert-only: it records an audit entry, shows a dashboard warning, and emails you (if alerts are on), but it never blocks anyone. See Distributed Attack Detection.
My site is behind Cloudflare. What do I need to configure?
Turn on Trust proxy headers so the plugin reads the real visitor IP from CF-Connecting-IP instead of treating every request as coming from Cloudflare’s IP. The header is only trusted when the request genuinely comes from a Cloudflare IP range. If you leave trust off behind a CDN, a single attacker can lock out everyone — the settings-page proxy health check warns you about this.
Can I use this with fail2ban?
Yes. Enable fail2ban logging to write failed-login and lockout events to a log file your fail2ban jail can watch, so offending IPs are banned at the network level. The log lines have a stable prefix and an ISO-8601 timestamp for easy matching.