Description
WPS Protect combines the three defences most WordPress sites install three separate plugins for — a hidden login URL, brute-force rate limiting, and HTTP security headers — into one lightweight plugin with no upsells and no performance overhead.
Login URL protection
- Move
wp-login.phpto a slug of your choice. - Choose what the old login URL returns: a 404, your home page, a custom URL, or a plain message.
- Slugs are validated before they are saved, against reserved WordPress paths, existing pages and post-type archives, so you cannot make your own content unreachable.
- Signed-out visitors to
/wp-admin/are sent to your login page with their destination preserved, instead of hitting a dead end. - AJAX,
admin-post.phpand REST endpoints stay reachable, so front-end forms, carts and the block editor keep working. - Password-protected posts, logout links, and password-reset emails all continue to work.
Emergency access
Losing your login URL should never mean editing the database. Three independent recovery routes:
- A one-time rescue URL that works from any page on your site.
define( 'WPSP_DISABLE_LOGIN_GATE', true );inwp-config.php.- WP-CLI:
wp wpsp login-url,wp wpsp set-slug,wp wpsp disable-gate.
The site administrator is emailed whenever the rescue URL is used.
Brute-force protection
- Escalating lockouts — the second offence lasts longer than the first.
- Tracks attempts per IP address and per username, so a distributed attack on one account is still caught.
- Covers every way WordPress checks a password: the login form, XML-RPC, the REST API, Application Passwords, WooCommerce and BuddyPress.
system.multicallis disabled, closing the XML-RPC amplification trick that turns one request into hundreds of password guesses.- Requests to your old login URL count as attacks — nothing legitimate knows that address any more.
- Proxy-aware: forwarding headers are trusted only from proxy ranges you configure, so nobody can spoof a header to dodge the limiter or get themselves allowlisted.
- Optional honeypot field, and generic error messages so the login and lost-password forms stop confirming which accounts exist.
- Locked-out requests return
429withRetry-After, so Cloudflare, Fastly or fail2ban can shed the attack at the edge.
Security headers
- X-Frame-Options, X-Content-Type-Options, Referrer-Policy, X-XSS-Protection, X-Permitted-Cross-Domain-Policies, Strict-Transport-Security, Content-Security-Policy (including Report-Only), Permissions-Policy, Cross-Origin-Opener-Policy, Cross-Origin-Resource-Policy and CORS.
- Headers are sent on the front end, the login screen, the dashboard and REST responses — not just the front end.
- Safe defaults: only headers that suit essentially every site are on out of the box. CSP, HSTS, Permissions-Policy and CORS ship switched off with a recommended value pre-filled, because guessing those breaks real sites.
- Every value is validated against header injection before it is saved, and risky settings are flagged with an explanation rather than silently accepted.
- HSTS is only sent over a secure connection and only after you confirm your subdomains are ready.
HTTPS enforcement
- Redirects insecure requests, honouring forwarding headers from trusted proxies so sites behind Cloudflare or a load balancer do not loop.
- Form submissions redirect with
307, so no data is lost. - Cannot be switched on from an insecure dashboard session, which is the usual way people lock themselves out.
Installation
- Upload the plugin files to
/wp-content/plugins/wps-protect-login-url-security-headers/, or install through the Plugins screen. - Activate the plugin.
- Go to WPS Protect in the dashboard menu.
Activation does not change your login URL. Nothing moves until you set a slug yourself, and the plugin issues a rescue URL at that moment.
FAQ
-
What happens if I forget my login URL?
-
Use the rescue URL shown when you saved the slug. If you no longer have it, add
define( 'WPSP_DISABLE_LOGIN_GATE', true );towp-config.php, or runwp wpsp login-urlover WP-CLI. Deactivating the plugin also restoreswp-login.php, and your settings survive deactivation. -
Will this break my caching plugin?
-
Login and REST requests are never cached, so brute-force protection and the login gate are unaffected. Security headers are sent by PHP, so on a page served from a full-page cache they may be served by your cache layer instead. If your host serves cached pages without invoking PHP, set the headers at the server level too.
-
Does it work behind Cloudflare?
-
Yes, but configure your proxy ranges under the brute-force settings first. Until you do, the plugin deliberately declines to block by IP address rather than risk locking out every visitor sharing a proxy address.
-
Will it conflict with another security plugin?
-
Enable “do not overwrite headers already set” if something else is already sending headers. Running two plugins that both hide the login URL is not supported — pick one.
-
I upgraded from 1.x. What changed for me?
-
Your settings are migrated automatically, including your login slug, HTTPS setting and any header values you saved. One thing intentionally does not carry over: 1.x applied a hard-coded
Content-Security-Policy: default-src 'self'whenever you had not set one, which blocked inline scripts and styles on most sites. That fallback is gone. If you want a CSP, set one explicitly on the Security Headers tab.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“WPS Protect: Login URL & Security Headers” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “WPS Protect: Login URL & Security Headers” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
2.0.0
Complete architectural rewrite, plus WordPress 7.1 compatibility.
Fixed (critical)
- Administrators were locked out of
/wp-admin/whenever a custom login URL was set. The capability check that guarded the dashboard was inverted: signed-in administrators were blocked while lower-privileged users were let through. - Every
admin-ajax.phprequest returned 404 with a custom login URL set, breaking the block editor, media uploads, WooCommerce and any plugin using AJAX. - The default
Content-Security-Policy: default-src 'self'was applied to every site that had not visited the settings screen, blocking inline scripts and styles used by WordPress core, most themes and most plugins. Access-Control-Allow-Origin: *is no longer sent by default.- The HTTPS redirect built its target from the unvalidated
Hostrequest header, allowing an open redirect, and issued it as a permanently cached301. - Requests were matched with an unanchored substring search, so any URL merely containing
wp-adminorwp-login.php— including ordinary posts and uploads — returned 404. exitinside thewp_logoutaction aborted password resets and any programmatic logout.- Password-protected posts (
action=postpass) no longer return 404. - Settings are no longer deleted when the plugin is deactivated. Deletion now happens on uninstall, as it should.
Added
- Brute-force protection: escalating lockouts, per-IP and per-username tracking, allowlist and blocklist, honeypot, generic error messages, and
429withRetry-After. Covers the login form, XML-RPC, REST, Application Passwords, WooCommerce and BuddyPress. - Emergency access: one-time rescue URL, a
wp-config.phpconstant, and WP-CLI commands. - Login slug validation against reserved paths, existing content and post-type archives.
- Security headers now apply to the login screen, dashboard and REST API, not only the front end.
- Content-Security-Policy-Report-Only, Cross-Origin-Opener-Policy, Cross-Origin-Resource-Policy and X-Permitted-Cross-Domain-Policies.
- Header value validation that rejects response splitting, with advisory warnings for risky values.
- Proxy-aware client IP resolution with configurable trusted ranges, and proxy-aware HTTPS detection.
- WP-CLI:
wp wpsp login-url,set-slug,disable-gate,rescue,unlock,lockouts,audit. - Audit log with retention policy and an IP anonymisation option.
Changed
- Rewritten as PSR-4 classes under
src/, separated into Login, Headers, SSL, Brute-force, Admin, Support, CLI and Compat modules. - Settings are saved through
admin-post.phpwith capability checks, nonce verification and a POST/Redirect/GET cycle, so refreshing no longer resubmits. X-XSS-Protectionnow defaults to0, matching current OWASP guidance. The previous1; mode=blockenabled a deprecated browser filter with vulnerabilities of its own.- HSTS requires an explicit confirmation and a secure connection before it is sent.
- Minimum requirements raised to WordPress 6.2 and PHP 7.2. WordPress 6.2 is required for the
%iidentifier placeholder in$wpdb->prepare(), which the brute-force log uses to quote table names.
Compatibility
- Tested against WordPress 7.1. The plugin ships no JavaScript or CSS, so the 7.1 iframed editor,
@wordpress/components, jQuery UI 1.14.2 and client-side media processing changes do not apply to it. Applying security headers acrosswp-adminis a separate, off-by-default option, because a restrictive Content-Security-Policy can interfere with the now always-iframed editor. - The
wpsplu_optionsrecord, thewpspluadmin page slug and its tab URLs are all preserved. Removed 1.x functions remain declared as no-op shims that raise a deprecation notice rather than a fatal error.
1.1
- Added Permissions-Policy header support
- Improved Content-Security-Policy configuration
- Enhanced login URL handling
- Added tab-based admin interface
- Updated security headers with recommended values
1.0
- Initial release with basic security features
