Small code base
About 10,000 lines of PHP in 22 files, under 1 MB, with no frameworks and no bundled libraries.
Login security for WordPress and WooCommerce in one small plugin: rate limiting, CAPTCHA, two-factor authentication, one-time login links, hardening, and an audit log, with no outside services.
I manage WordPress sites for clients, and the login page is where most attacks start. The popular security plugins each did part of the job, but covering everything meant running several of them, with dashboards full of upsells, extra scripts on the login page, and settings to re-check after every update. I wanted one plugin that did exactly what my clients' sites need, and nothing else.
So I built it. Login Guard brings together the features I relied on from the best-known security plugins—rate limiting and lockouts, CAPTCHA, two-factor authentication, one-time login links, hardening, and an audit log—in one small plugin I write, test, and update myself.
About 10,000 lines of PHP in 22 files, under 1 MB, with no frameworks and no bundled libraries.
With the built-in math CAPTCHA, the login form loads no scripts at all. The only script is a small QR-code generator on the two-factor setup screen, plus Cloudflare's own if you choose Turnstile.
It runs entirely on your server, with no cloud account, no tracking, and no upsells.
The defaults protect the site the moment it's installed. Each settings tab saves on its own, and a Recommended list shows anything still switched off, with a one-click Enable.
Login Guard is free software, licensed under GPL-2.0-or-later, like WordPress itself.
Login security for WordPress and WooCommerce sites: rate limiting, CAPTCHA, two-factor authentication, one-time login links, hardening, an audit log, and recovery tools, all in one plugin with no outside services.
Settings live under Login Guard in the WordPress admin menu (also reachable at Settings → Login Guard).
Updates arrive through the normal WordPress Updates screen. You do not need to configure anything for the protection to work: the defaults turn on rate limiting, hardening, the audit log, honeypot fields and breached-password warnings.
Open Login Guard in the admin menu. Each tab has its own Save button at the bottom; saving one tab never changes another.
Before you change anything
Add your own IP address on the Whitelist tab. A whitelisted address is never rate limited or locked out, so you cannot lock yourself out while experimenting.
The panel at the top of every tab reports how the plugin sees visitors: whether the site sits behind Cloudflare or another proxy, which header the real visitor IP comes from, and whether that is verified. If it says the IP is being read from a proxy header that cannot be verified, ask your host or contact me; lockouts could otherwise apply to the wrong address.
Below it, Recommended lists protections that are currently off, each with an Enable button that switches that one setting on. There is no score to chase; an empty list simply means everything recommended is already on.
Use Diagnostics → Send test summary now to confirm mail delivery.
Two providers: a built-in math question (no external calls) or Cloudflare Turnstile when you paste a site key and secret. You choose which forms it protects: WordPress login, registration and lost-password, and the matching WooCommerce forms. A solved challenge stays valid for the number of minutes you set, so a user who mistypes a password is not asked again immediately.
After the number of failed attempts you allow, the visitor's IP is locked out for the lockout period. Attempts through XML-RPC and WooCommerce count too. Optional extras:
admin; real users rarely mistype their username.The Active Lockouts tab lists every current lockout with an Unlock button and a Block button that moves the IP to the permanent block-list.
| Option | What it does |
|---|---|
| XML-RPC | Disables XML-RPC authentication (recommended) or blocks the endpoint entirely. Jetpack and some mobile apps need XML-RPC; "disable auth" keeps them working while stopping password guessing. |
| Generic login error | Shows the same message for a wrong username and a wrong password, so the form no longer confirms which accounts exist. |
| Honeypot fields | Invisible fields that bots fill in and people never see. A filled honeypot rejects the submission. |
| Breached-password check | Checks passwords against the Have I Been Pwned database using k-anonymity: only the first five characters of a hash leave the site. Warn or block on login; reject on reset and profile. |
| Block user enumeration | Hides author archives, ?author=1 redirects and the public users REST endpoint. |
| Disable file editing | Removes the theme and plugin code editors from wp-admin. |
| Disable application passwords | Turns off REST application passwords when nothing uses them. |
| Strip version and pingback headers | Removes the WordPress version and X-Pingback header from responses. |
| Disable comments site-wide | Closes comments everywhere and removes the comment menus, widgets and endpoints. Existing comments are kept. |
| Block probe files (.htaccess) | Denies public access to wp-admin/install.php, readme.html, license.txt and debug.log. Apache and LiteSpeed only. |
Time-based codes from any authenticator app (Google Authenticator, 1Password, Authy, Microsoft Authenticator). No external service is involved.
After a correct password you are asked for the current code, or a backup code. Tick Trust this browser to skip the code on that browser for the number of days set. Administrators can require two-factor for chosen roles with a grace period; users in those roles see a reminder until they enrol, and are asked to enrol at sign-in once the grace period ends. Nobody is locked out.
Use a backup code, or ask an administrator to click Reset 2FA on your profile or on the Users list. You can then enrol again.
WP Engine and WooCommerce
Both the WordPress login and the WooCommerce My Account login are covered. On WP Engine the code screen is served from a front-end route automatically, because WP Engine's cache can interfere with wp-login.php.
A password-free sign-in. The user clicks Email me a login link above the login form, enters their username or email, and a single-use link arrives by email. The link is bound to the IP that requested it, expires after the minutes you set, and is consumed the moment it is used. Requests go through the same honeypot and rate limits as the login form, and the response wording is identical whether or not the account exists.
Links are delivered with the site's normal mail. If the site has no working mail transport, nothing arrives. Check Diagnostics or your SMTP plugin first.
Adds a Google button to the login forms. Requires a Google Cloud OAuth client (one per site), which you create in Google Cloud. Options: automatically create accounts for new Google users with a chosen role, restrict to specific email domains, and count a Google sign-in as the second factor (default on, since Google enforces its own 2FA). How Google data is handled is covered below the guide.
admin.Before you block an IP
An IP address may be shared by many people (offices, schools, mobile carriers, VPNs), and blocking one does not stop a bot from using others. If legitimate users have trouble logging in after you add an IP, remove it from the block-list.
Brand the login page without another plugin: logo, background colour or image, button and link colour (inherit from the theme or set your own), form card colour and corner radius, the magic link button style, and a custom CSS box for anything else.
Every security event is recorded with time, event, username, IP and (behind Cloudflare) country: logins, failures, lockouts, reset emails, CAPTCHA and honeypot rejections, blocked IPs, registrations, two-factor and magic-link events, plugin/theme/core changes and recovery actions. Filter by event or IP, export CSV, and set how many days to keep. The Worst offending IPs table at the top shows the last 30 days with a Block button per row.
A quick read of the install for sites you cannot inspect over SSH: file and directory permissions, whether PHP runs as the file owner, the file editor and debug-display constants, whether debug.log, readme.html and install.php are publicly reachable, directory listing on uploads, HTTPS, pending core updates and the PHP version. Each row is marked OK, Check, Fix or Info. A Fix button appears only where a permission change is possible; it runs chmod on that single path and reports "denied" on hosts that forbid it.
Administrator-only tools for the bad day. Each one asks you to type a confirmation word and is written to the audit log.
| Tool | Effect |
|---|---|
| Force sign-out everywhere | Ends every session for every user, including yours. |
| Rotate security keys and salts | Backs up wp-config.php, then writes new keys. Everyone is signed out; stolen cookies stop working. |
| Reinstall WordPress core | Downloads the same version again and replaces core files. Content and settings are untouched. |
| Reinstall plugins from WordPress.org | Re-downloads the plugins you tick. Self-hosted plugins and the ManageWP worker are never touched. |
| Reset two-factor enrolment | For all users, a role or one user. Two-factor stays on; affected users enrol again at next sign-in, nobody is locked out, and the notification address is emailed. |
| Require password reset | Users in the chosen role (or everyone except you) are signed out and must set a new password through the lost-password form. |
wp tlg 2fa status <user> # enrolment state
wp tlg 2fa reset <user> # clear a user's enrolment
wp tlg 2fa list # everyone enrolled
wp tlg 2fa code <user> # current code (support use only)
wp tlg magic <user> [--send] # print or email a one-time login link
| Constant | Effect |
|---|---|
TLG_DISABLED | Switches all protection off (updates still run). Use when locked out. |
TLG_2FA_DISABLED | Stops enforcing two-factor without deleting anyone's enrolment. |
TLG_BYPASS_IP | Whitelists one IP at the PHP level, before settings load. |
TLG_DEFAULTS | Fleet defaults merged under the saved settings. |
TLG_GUIDE_URL | Where the User guide links point. |
Renaming the plugin folder over SFTP disables it entirely.
Wait for the lockout period, use the magic link if it is enabled, or contact me. Over SFTP, define( 'TLG_DISABLED', true ); in wp-config.php switches protection off instantly.
Check Diagnostics → Server Clock. Codes need the server within 30 seconds of real time.
The site cannot send mail. Check the SMTP plugin or ask your host; Diagnostics → Send test summary now is a quick test.
The host blocks the site from requesting its own pages (loopback). The rest of the report is still valid.
The Status panel will say whether a proxy or CDN is in front of the site and whether it is verified. Ask your host; this is a hosting-side setting.
When someone visiting a site that runs Threshold Login Guard clicks Sign in with Google, the plugin begins a standard OpenID Connect (OAuth 2.0) sign-in with Google. Threshold Login Guard requests only two non-sensitive scopes:
Threshold Login Guard does not request access to Gmail, Google Drive, Contacts, Calendar, Photos, or any other Google service. It cannot read, create, modify, or delete anything in a user's Google Account.
Once Google confirms the identity, the plugin matches the verified email address to a WordPress user account on that same website and signs the person in. If no matching account exists, the site owner can allow one to be created automatically. Administrators and editors are held to a stricter rule: Google sign-in is refused until they confirm their existing WordPress password once, which prevents anyone from attaching an outside Google account to a privileged account.
Sign-in happens directly between the visitor's browser, the website being signed in to, and Google. No Google user data is transmitted to Spencer Taylor, to Threshold Solutions, or to any third party, and none is used for advertising or sold.
The only Google information retained is stored in that individual website's own WordPress database:
The short-lived Google token used to complete the sign-in is used once and discarded. It is never written to disk or stored in the database, and Threshold Login Guard does not request or keep refresh tokens, so it has no ongoing access to any Google Account.
A user can unlink their Google Account at any time from their WordPress profile page, or ask the site owner to delete their account, which removes the stored identifier and email address.
Threshold Login Guard's use of information received from Google APIs adheres to the Google API Services User Data Policy, including the Limited Use requirements.
Threshold Login Guard is free software, released under GPL-2.0-or-later. Download the current version at the top of this page. Sites that run it get updates through the normal WordPress Updates screen, from my own update server.
Threshold Login Guard is developed and supported by Spencer Taylor, Threshold Solutions, South Jersey, USA. For questions about the plugin, about Google sign-in, or to request deletion of data held about you, use the contact page.
Your use of Threshold Login Guard is governed by the Privacy Policy and the Terms of Service.
Have an idea for a feature you'd like to see in Login Guard? Drop me a note.
Ask me about the plugin, Google sign-in, or the security of your own site.