Threshold Plugins

Threshold Login Guard

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.

  • Version 1.14.2
  • Updated October 1, 2026
  • WordPress 6.0+
  • PHP 7.4+
  • GPL-2.0-or-later
A WordPress login form beside a padlock

Why I built Login Guard

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.

Small code base

About 10,000 lines of PHP in 22 files, under 1 MB, with no frameworks and no bundled libraries.

Almost no JavaScript

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.

No outside services

It runs entirely on your server, with no cloud account, no tracking, and no upsells.

Easy to use

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.

User guide

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).

Getting started

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.

Status & Recommended

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.

General & notifications

  • Enable protection is the master switch. Turning it off disables every module except updates.
  • Notifications send email to the address you enter (or the site admin email when blank). Lockout alerts go out when an IP is locked out.
  • Summary email sends a daily or weekly report at 7:00 site time: totals by event, the worst offending IPs with country, plugin/theme/core changes and any red Site Check items. Nothing is sent when nothing happened.
  • Instant alerts cover an administrator signing in from a new country, plugin/theme/core changes, a two-factor reset and a salt rotation.
  • Export / Import moves settings between sites as a JSON file. Two-factor secrets are never included.

Use Diagnostics → Send test summary now to confirm mail delivery.

CAPTCHA

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.

Rate limiting & lockouts

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:

  • Instant lockout on invalid username: an attempt with a username that does not exist locks the IP immediately. Bots guess admin; real users rarely mistype their username.
  • Password-reset throttling: limits reset emails per user and per IP so the lost-password form cannot be used to flood someone's inbox.

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.

Hardening

Hardening options
OptionWhat it does
XML-RPCDisables 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 errorShows the same message for a wrong username and a wrong password, so the form no longer confirms which accounts exist.
Honeypot fieldsInvisible fields that bots fill in and people never see. A filled honeypot rejects the submission.
Breached-password checkChecks 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 enumerationHides author archives, ?author=1 redirects and the public users REST endpoint.
Disable file editingRemoves the theme and plugin code editors from wp-admin.
Disable application passwordsTurns off REST application passwords when nothing uses them.
Strip version and pingback headersRemoves the WordPress version and X-Pingback header from responses.
Disable comments site-wideCloses 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.

Two-factor authentication

Time-based codes from any authenticator app (Google Authenticator, 1Password, Authy, Microsoft Authenticator). No external service is involved.

Enrolling

  1. Go to Users → Profile → Two-Factor Authentication.
  2. Scan the QR code (or type the key) into your authenticator app.
  3. Enter the six-digit code the app shows. Two-factor turns on only after a correct code.
  4. Save the ten backup codes somewhere safe. Each works once.

Signing in

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.

Lost phone

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.

Magic link login

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.

  • Where to offer it: the WordPress login, the WooCommerce login, and a Send login link action on the Users screen for administrators.
  • Limits: link lifetime, one request per user per N minutes, and a per-IP cap inside the lockout window.
  • Who may use it: leave all roles unticked to allow everyone, or restrict to chosen roles.
  • Two-factor: by default a magic link replaces the password only; enrolled users are still asked for their code. Switch "counts as the second factor" on only if you accept that mailbox access alone signs the user in.
  • Button style: colours, border and corner radius are on the Login Screen tab under Login Link Button.

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.

Sign in with Google

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.

Whitelist, block-list, usernames, disposable emails

  • Whitelist: IPs or CIDR ranges that are never limited or locked out. Put your office and home here.
  • Block-list: IPs or ranges refused at login (or site-wide, if you choose). The Audit Log suggests the worst offenders with a one-click Block.
  • Blocked usernames: usernames that cannot be registered or used to log in, such as admin.
  • Disposable emails: rejects throwaway email domains on registration and WooCommerce checkout, with your own allow and block lists on top of the built-in list.

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.

Login screen

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.

Audit log

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.

Site Check

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.

Admin Menu

Simplify the dashboard for clients. Choose who the hiding applies to: Everyone but me (the user who saves becomes the owner), everyone except a list of exempt usernames, or only chosen roles. Then tick any menu or submenu item to hide, optionally hide the Login Guard menu itself, and hide the front-end toolbar for chosen roles. Hiding is cosmetic: it removes menu entries, not capabilities, so a direct URL still works for a user whose role allows it.

Recovery tools

Administrator-only tools for the bad day. Each one asks you to type a confirmation word and is written to the audit log.

Recovery tools
ToolEffect
Force sign-out everywhereEnds every session for every user, including yours.
Rotate security keys and saltsBacks up wp-config.php, then writes new keys. Everyone is signed out; stolen cookies stop working.
Reinstall WordPress coreDownloads the same version again and replaces core files. Content and settings are untouched.
Reinstall plugins from WordPress.orgRe-downloads the plugins you tick. Self-hosted plugins and the ManageWP worker are never touched.
Reset two-factor enrolmentFor 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 resetUsers in the chosen role (or everyone except you) are signed out and must set a new password through the lost-password form.

WP-CLI & wp-config switches

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
wp-config.php constants
ConstantEffect
TLG_DISABLEDSwitches all protection off (updates still run). Use when locked out.
TLG_2FA_DISABLEDStops enforcing two-factor without deleting anyone's enrolment.
TLG_BYPASS_IPWhitelists one IP at the PHP level, before settings load.
TLG_DEFAULTSFleet defaults merged under the saved settings.
TLG_GUIDE_URLWhere the User guide links point.

Renaming the plugin folder over SFTP disables it entirely.

Troubleshooting

I locked myself out

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.

Two-factor codes keep failing for everyone

Check Diagnostics → Server Clock. Codes need the server within 30 seconds of real time.

The magic link email never arrives

The site cannot send mail. Check the SMTP plugin or ask your host; Diagnostics → Send test summary now is a quick test.

Site Check says "Could not check"

The host blocks the site from requesting its own pages (loopback). The rest of the report is still valid.

Everything shows the wrong visitor IP

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.

Sign in with Google and your data

How Sign in with Google works

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:

  • openid — a stable, anonymous identifier for the Google Account.
  • email — the email address on that Google Account, and whether Google has verified it.

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.

What Google information is stored, and where

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 email address from the Google Account, saved as the WordPress user's email address so the person can be recognised on return.
  • The Google account identifier (the OpenID sub value), saved so a returning user is matched to the correct account even if their email address later changes.

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.

Licence, support, and policies

Licence and availability

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.

Support and contact

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.

Have an idea for a feature you'd like to see in Login Guard? Drop me a note.

Questions about Login Guard?

Ask me about the plugin, Google sign-in, or the security of your own site.