Title: Nahnu Login Shield
Author: Jaime Alnassim
Published: <strong>October 14, 2026</strong>
Last modified: October 14, 2026

---

Search plugins

![](https://ps.w.org/nahnu-login-shield/assets/banner-772x250.png?rev=3745237)

![](https://ps.w.org/nahnu-login-shield/assets/icon-256x256.png?rev=3745237)

# Nahnu Login Shield

 By [Jaime Alnassim](https://profiles.wordpress.org/ja1me4/)

[Download](https://downloads.wordpress.org/plugin/nahnu-login-shield.1.0.11.zip)

 * [Details](https://wordpress.org/plugins/nahnu-login-shield/#description)
 * [Reviews](https://wordpress.org/plugins/nahnu-login-shield/#reviews)
 *  [Installation](https://wordpress.org/plugins/nahnu-login-shield/#installation)
 * [Development](https://wordpress.org/plugins/nahnu-login-shield/#developers)

 [Support](https://wordpress.org/support/plugin/nahnu-login-shield/)

## Description

**Nahnu Login Shield** does one thing and does it well: protect every WordPress 
login entry point. It is not a general security plugin. It is a login security plugin—
hyperfocused on the login form, WooCommerce My Account, XML-RPC, the REST API, and
all the form plugins your site might use.

No subscription. No license key. No upsell. Everything is included free.

#### Brute-force lockout

 * Configurable attempt limit, time window, and short lockout duration.
 * **Escalating lockout** — after N short lockouts the IP receives a long lockout(
   default 24 h).
 * **Per-username lockout** — if the same username is tried from many different 
   IPs (a distributed botnet), the username itself is locked across all IPs after
   a configurable threshold.
 * **Honeypot usernames** — any attempt using a known-bad username (admin, root,
   administrator, test…) instantly locks the IP and the username globally. One attempt
   is enough.
 * CIDR-supported IP blocklist and IP whitelist (IPv4 and IPv6).
 * Manual unlock button next to any locked IP in the log.

#### Early blocking

An optional must-use plugin (mu-plugin) can fire before WordPress fully loads. It
is **off by default and never installed automatically**: enable it from the General
tab by clicking “Install early-block mu-plugin”, which copies `nahnls-early-block.
php` into your must-use plugins directory (`WPMU_PLUGIN_DIR`). Once installed, locked-
out IPs are turned away at the door — no PHP framework, no database writes, no WooCommerce—
for a fraction of the normal request cost. Honeypot IPs receive a full static block
page for all site pages during the lockout period. Clicking “Remove” (or deactivating/
deleting the plugin) deletes the file again.

#### CAPTCHA / Verification providers

Choose one provider site-wide. The widget appears on every protected form automatically.

 * **OOPSpam** — checkbox widget powered by OOPSpam Anti-Spam. Adds login-specific
   overrides: block VPN/proxy, datacenter IPs, and disposable email addresses.
 * **reCAPTCHA v2** — Google checkbox challenge.
 * **reCAPTCHA v3** — Google invisible score-based. No user interaction required.
 * **hCaptcha** — privacy-first, free tier.
 * **Cloudflare Turnstile (managed)** — Cloudflare decides whether to show a challenge
   based on risk signals.
 * **Cloudflare Turnstile (invisible)** — entirely silent.
 * **Friendly Captcha** — EU-hosted, proof-of-work, no cookies. GDPR-ready.
 * **None** — disable the widget entirely. Geo-blocking, lockout, and honeypot still
   apply.

#### Two-factor authentication

 * When 2FA is on, every user in the roles you select (all roles by default) is 
   asked for a one-time code after their password, on every login.
 * Email codes are always on as the baseline – nobody can skip the second step. 
   Users can add an authenticator app (TOTP) or a passkey for stronger protection;
   the code is emailed automatically only when they have nothing stronger.
 * Users cannot turn 2FA off for themselves; administrators can reset another user’s
   methods (for example after a lost phone), which falls back to email codes.
 * Backup codes as a universal fallback.
 * Emergency recovery: `define( 'NAHNLS_DISABLE_2FA', true );` in wp-config.php 
   switches the 2FA check off.
 * Challenge happens on wp-login.php, or right inside WooCommerce My Account for
   customers.
 * Per-user setup on a dedicated Login & security page (wp-admin) or the My Account
   tab – codes are confirmed inline, with a scannable QR code and manual key.

#### Passwordless sign-in

 * **Passkeys (WebAuthn)** – sign in with Touch ID, Windows Hello, a phone or a 
   security key. Browser autofill (conditional mediation) supported. Built-in verifier(
   ES256, RS256, Ed25519), no external service.
 * **Magic links** – one-time email login links; hashed at rest, rate-limited, go
   through lockout, geo and CAPTCHA checks. Per-user opt-out.
 * Both available on wp-login.php and the WooCommerce My Account form; role allow-
   lists.

#### White-label emails

 * **Email branding** (Settings tab) – the 2FA code, magic link and signup confirmation
   emails use your sender name and address, logo, accent and header colours and 
   footer text. Subject, heading and intro of each email are editable with placeholders;
   preview in the browser and send yourself a test email. No plugin branding in 
   emails unless you switch on the optional credit line.

#### Access control

 * Restrict roles to IP addresses / CIDR ranges (IPv4 + IPv6).
 * Login and logout redirects per role.
 * XML-RPC kill switch (403 before WordPress loads).
 * Signup email verification with automatic pruning of unconfirmed accounts.
 * Security checklist with a score and one-click fixes.

#### Geo-fencing

Reads the visitor country from CDN headers with zero latency and zero API quota.
Supported headers: CF-IPCountry (Cloudflare), CDN-Country (Bunny.net), CloudFront-
Viewer-Country (AWS CloudFront), X-Vercel-IP-Country (Vercel), X-Country-Code (Fastly/
Akamai), X-Geo-Country (generic CDN).

Falls back to freeipapi.com or ipinfo.io (both free) with a configurable transient
cache when no CDN header is present.

#### Edge WAF integration

 * **Bunny.net WAF** — push locked-out IPs to Bunny WAF with one click. Blocks them
   at the CDN edge before they reach your server.
 * **Cloudflare WAF (IP Access Rules)** — same for Cloudflare. Uses the IP Access
   Rules API (not the 5-rule WAF limit). Auto-expiry via WP-Cron.
 * Both integrations support triggering on honeypot hits, on brute-force lockout,
   or both.
 * Edge block log in the admin with IP, provider, reason, age, expiry, and one-click
   unblock.

#### JavaScript presence check

A hidden field filled by a small inline script gates every submission. Headless 
bots that skip JavaScript execution are blocked before CAPTCHA or lockout checks
run — saving API quota.

#### Custom login URL

Optionally rename /wp-login.php to a secret path. Requests to the old URL return
a configurable response: 404, redirect to home, or redirect to a custom URL.

#### REST API protection

 * Block unauthenticated access to /wp-json/wp/v2/users (username enumeration).
 * Apply lockout and IP block rules to REST API authentication.
 * Optionally disable Application Passwords.

#### Session security

 * Idle session timeout with configurable duration and a 2-minute warning dialog.
 * Force logout all sessions immediately with one button (useful during an incident).

#### Login notifications

 * Notify each user by email on account login with IP and device info.
 * Alert admin emails whenever any administrator logs in.
 * New-country alert: email the first time a user logs in from a previously unseen
   country.
 * Weekly security digest: failed attempts, lockouts, honeypot hits, top attacking
   IPs, top targeted usernames.

#### WooCommerce My Account

Extend all protections – geo-blocking, lockout, honeypot, CAPTCHA, JS check, passkeys,
magic links – to the WooCommerce My Account login, registration, lost-password, 
checkout, and order-tracking forms. Each form has its own toggle. Customers get 
a **Login & security** tab in My Account to manage passkeys, two-factor authentication
and sign-in options without ever seeing wp-admin, and complete the 2FA challenge
on the My Account page itself. The same panel is available anywhere via the `[nahnls_account_security]`
shortcode.

#### Form plugin integrations

Three form integrations are supported, each with its own toggle on the Integrations
tab: **Bricks Builder** forms (enabled per form from the Bricks form element’s Spam
Protection panel), **SureForms** (all forms, or only the forms you pick), and the
native **WordPress comment form**. Each adds the JS marker check and the configured
CAPTCHA widget to the form and validates it server-side on submit. Any other form,
theme template or headless front end can integrate through the developer API: template
tags, `[nahnls_passkey_button]` / `[nahnls_magic_link_form]` shortcodes, 20+ actions
and filters, REST routes under `nahnls/v1` and the `window.NAHNLSAuth` JavaScript
API (see docs/API.md).

#### Full event log

 * Search by IP, username, or detail text.
 * Filter by event type (fail, lockout, honeypot, geo block, success, captcha fail…).
 * Date range picker.
 * CSV export.
 * Log detail column shows the source form or endpoint for every event (WooCommerce
   My Account, REST API, XML-RPC, Bricks Builder Form, WP Login form, etc.).
 * Configurable retention period and row cap.

#### Analytics

 * Daily activity chart (30-day view).
 * Attack time-of-day heatmap.
 * Event breakdown donut chart.
 * Top attacking IPs table.
 * Top targeted usernames table.
 * All data is local — no external analytics service.

#### MainWP extension

A companion extension (Nahnu Login Shield — MainWP Extension) is available separately
and allows site managers to view login shield data — lockouts, edge WAF blocks, 
recent events, settings summary — across all connected child sites from the MainWP
dashboard, with a widget on each site overview page and a per-site detail tab.

#### Privacy

Nahnu Login Shield stores IP addresses and usernames in a local WordPress database
table for the event log. The retention period and row cap are configurable. No data
is sent to any Nahnu server. Third-party CAPTCHA providers and IP lookup services
receive only the data required for their function, per their own privacy policies.
Deleting the plugin removes all stored data via the included uninstall.php.

#### External services

Nahnu Login Shield works fully offline by default. Each service below is contacted**
only** when the site administrator explicitly selects or configures it in the plugin
settings. No data is ever sent to a Nahnu server.

 * **Google reCAPTCHA (v2 / v3)** — Used as the CAPTCHA provider when selected on
   the Widget tab. The visitor’s browser loads the widget script from `https://www.
   google.com/recaptcha/api.js` on protected forms, and Google receives the visitor’s
   IP address and browser information per its own policy. On every form submit the
   plugin sends the reCAPTCHA response token together with your reCAPTCHA secret
   key server-to-server to `https://www.google.com/recaptcha/api/siteverify`. [Terms of Service](https://policies.google.com/terms)·
   [Privacy Policy](https://policies.google.com/privacy)
 * **hCaptcha** — Used as the CAPTCHA provider when selected. The visitor’s browser
   loads the widget from `https://js.hcaptcha.com/1/api.js` on protected forms (
   hCaptcha receives the visitor’s IP and browser data). On form submit the plugin
   sends the hCaptcha response token and your secret key server-to-server to `https://
   api.hcaptcha.com/siteverify`. [Terms of Service](https://www.hcaptcha.com/terms)·
   [Privacy Policy](https://www.hcaptcha.com/privacy)
 * **Cloudflare Turnstile** — Used as the CAPTCHA provider when selected (managed
   or invisible mode). The visitor’s browser loads the widget from `https://challenges.
   cloudflare.com/turnstile/v0/api.js` on protected forms (Cloudflare receives the
   visitor’s IP and browser data). On form submit the plugin sends the Turnstile
   token and your secret key server-to-server to `https://challenges.cloudflare.
   com/turnstile/v0/siteverify`. [Terms of Service](https://www.cloudflare.com/website-terms/)·
   [Privacy Policy](https://www.cloudflare.com/privacypolicy/)
 * **Friendly Captcha** — Used as the CAPTCHA provider when selected. The widget
   script itself is bundled with the plugin (no CDN), but the visitor’s browser 
   fetches a proof-of-work puzzle from `api.friendlycaptcha.com` while the form 
   is displayed (Friendly Captcha receives the visitor’s IP address). On form submit
   the plugin sends the solved puzzle token (`frc-captcha-solution`) plus your site
   key and your Friendly Captcha API key server-to-server to `https://global.frcapi.
   com/api/v2/captcha/siteverify`. [Terms of Service](https://friendlycaptcha.com/legal/terms/)·
   [Privacy Policy](https://friendlycaptcha.com/legal/privacy-end-users/)
 * **OOPSpam Anti-Spam** — Used as the verification provider when selected; requires
   the separate OOPSpam Anti-Spam plugin with its own API key. On login/registration
   submit (and optionally at widget render) the plugin calls the OOPSpam plugin,
   which sends the visitor’s IP address and the submitted username/email, together
   with your OOPSpam API key, to the OOPSpam API to obtain a spam/VPN/data-centre
   score. [Terms of Service](https://www.oopspam.com/terms) · [Privacy Policy](https://www.oopspam.com/privacy)
 * **Bunny.net API** (`https://api.bunny.net`) — Used only when the Bunny WAF edge-
   blocking integration is enabled on the Edge Blocking tab. When a lockout or honeypot
   event fires, the plugin sends the blocked IP address, an expiry timestamp and
   a short note, authenticated with your Bunny API key, to the Bunny WAF IP-block
   endpoint for your pull zone. The “Test connection” button reads your pull zone
   details with the same key. [Terms of Service](https://bunny.net/tos/) · [Privacy Policy](https://bunny.net/privacy)
 * **Cloudflare API** (`https://api.cloudflare.com/client/v4`) — Used only when 
   the Cloudflare WAF edge-blocking integration is enabled. When a lockout or honeypot
   event fires, the plugin creates an IP access rule containing the blocked IP address
   and a note, authenticated with your Cloudflare API token; a scheduled task later
   deletes that rule when the block expires, and the “Test connection” button verifies
   the token. [Terms of Service](https://www.cloudflare.com/website-terms/) · [Privacy Policy](https://www.cloudflare.com/privacypolicy/)
 * **freeipapi.com** — Geo-lookup fallback, used only when geo-blocking is enabled
   and “freeipapi” is selected as the API source and no CDN country header is present.
   On a login attempt the plugin sends the visitor’s IP address (and your optional
   API key as a bearer token) to `https://free.freeipapi.com/api/json/{ip}` to learn
   its country code; results are cached locally per IP. [Terms of Use](https://freeipapi.com/terms)·
   [Privacy Policy](https://freeipapi.com/privacy)
 * **ipinfo.io** — Geo-lookup fallback, used only when geo-blocking is enabled and“
   ipinfo” is selected as the API source. On a login attempt the plugin sends the
   visitor’s IP address (and your optional API token) to `https://api.ipinfo.io/
   lite/{ip}` to learn its country code; results are cached locally per IP. [Terms of Service](https://ipinfo.io/terms-of-service)·
   [Privacy Policy](https://ipinfo.io/privacy-policy)
 * **Self-hosted Cap and mCaptcha instances** — If you select the Cap or mCaptcha
   provider you must enter the URL of an instance **you** host. The visitor’s browser
   loads that instance’s widget script, and on form submit the plugin sends the 
   challenge token and your secret key server-to-server to `{your-instance}/siteverify`(
   Cap) or `{your-instance}/api/v1/pow/siteverify` (mCaptcha). No third party is
   involved unless you point the URL at one; see the [Cap](https://trycap.dev/) 
   and [mCaptcha](https://mcaptcha.org/) documentation for their respective terms.
 * **ALTCHA** — If selected, verification is handled locally by the separately installed
   ALTCHA plugin; this plugin makes no external request for it.

Country flags shown in the admin log are SVG files bundled with the plugin (`assets/
img/flags/`); no flag CDN or other external image service is used.

#### Bundled third-party libraries

No scripts are loaded from a CDN. These libraries ship inside the plugin (assets/
vendor/ unless noted otherwise):

 * **Chart.js 4.4.1** — MIT License, https://www.chartjs.org. Used on the Analytics
   admin tab.
 * **Friendly Captcha widget (friendly-challenge 0.9.18)** — MIT License, https://
   github.com/FriendlyCaptcha/friendly-challenge. Used only when Friendly Captcha
   is the selected CAPTCHA provider.
 * **flag-icons** (country flag SVGs in assets/img/flags/) — MIT License, https://
   github.com/lipis/flag-icons. Displayed in the admin event log; loaded from the
   plugin folder, never from a CDN.

## Screenshots

[⌊General tab — today's stats, the master switch, which forms to protect, the dashboard
widget, the optional early-block mu-plugin and the JavaScript check.⌉⌊General tab—
today's stats, the master switch, which forms to protect, the dashboard widget, 
the optional early-block mu-plugin and the JavaScript check.⌉[

General tab — today’s stats, the master switch, which forms to protect, the dashboard
widget, the optional early-block mu-plugin and the JavaScript check.

[⌊Widget tab — pick the CAPTCHA provider (OOPSpam, reCAPTCHA v2/v3, hCaptcha, Turnstile,
Friendly Captcha) with a live preview.⌉⌊Widget tab — pick the CAPTCHA provider (
OOPSpam, reCAPTCHA v2/v3, hCaptcha, Turnstile, Friendly Captcha) with a live preview
.⌉[

Widget tab — pick the CAPTCHA provider (OOPSpam, reCAPTCHA v2/v3, hCaptcha, Turnstile,
Friendly Captcha) with a live preview.

[⌊Geo tab — block the login form by country using CDN geo headers or an IP lookup
API.⌉⌊Geo tab — block the login form by country using CDN geo headers or an IP lookup
API.⌉[

Geo tab — block the login form by country using CDN geo headers or an IP lookup 
API.

[⌊Edge Blocking tab — push lockouts and honeypot hits to the Bunny.net or Cloudflare
WAF so attackers are stopped at the edge.⌉⌊Edge Blocking tab — push lockouts and
honeypot hits to the Bunny.net or Cloudflare WAF so attackers are stopped at the
edge.⌉[

Edge Blocking tab — push lockouts and honeypot hits to the Bunny.net or Cloudflare
WAF so attackers are stopped at the edge.

[⌊Lockout tab — failed-attempt thresholds, escalation to long bans, the lockout 
message and proxy header settings.⌉⌊Lockout tab — failed-attempt thresholds, escalation
to long bans, the lockout message and proxy header settings.⌉[

Lockout tab — failed-attempt thresholds, escalation to long bans, the lockout message
and proxy header settings.

[⌊Integrations tab — WooCommerce My Account forms plus Bricks Builder, SureForms
and WordPress comment protection.⌉⌊Integrations tab — WooCommerce My Account forms
plus Bricks Builder, SureForms and WordPress comment protection.⌉[

Integrations tab — WooCommerce My Account forms plus Bricks Builder, SureForms and
WordPress comment protection.

[⌊2FA tab — two-factor authentication for selected roles, with email codes as the
baseline and authenticator apps or passkeys as stronger options.⌉⌊2FA tab — two-
factor authentication for selected roles, with email codes as the baseline and authenticator
apps or passkeys as stronger options.⌉[

2FA tab — two-factor authentication for selected roles, with email codes as the 
baseline and authenticator apps or passkeys as stronger options.

[⌊Passwordless tab — magic login links and passkeys (WebAuthn).⌉⌊Passwordless tab—
magic login links and passkeys (WebAuthn).⌉[

Passwordless tab — magic login links and passkeys (WebAuthn).

[⌊Settings tab — log retention and white-label email branding for every email the
plugin sends.⌉⌊Settings tab — log retention and white-label email branding for every
email the plugin sends.⌉[

Settings tab — log retention and white-label email branding for every email the 
plugin sends.

## Installation

 1. Upload the `nahnu-login-shield` folder to `/wp-content/plugins/`.
 2. Activate through **Plugins** in the WordPress admin.
 3. Go to **Settings > Login Shield** to configure.

**Quick start:**

 1. On the **Widget** tab, choose a CAPTCHA provider (or leave it as None to use lockout
    only).
 2. On the **Lockout** tab, review the attempt thresholds.
 3. On the **Geo** tab, enable geo-blocking and select blocked countries if needed.
 4. On the **Integrations** tab, enable protections for WooCommerce My Account, Bricks
    Builder forms, SureForms and WordPress comments as needed.
 5. On the **Edge Blocking** tab, connect your Bunny.net or Cloudflare account to push
    blocks to the edge.

To use OOPSpam, install and activate the OOPSpam Anti-Spam plugin and enter your
API key there first. Nahnu Login Shield detects it automatically.

## FAQ

### Do I need OOPSpam installed?

No. OOPSpam is one of seven CAPTCHA provider options. All other providers and all
other features work independently.

### Does this plugin slow down my site?

No. Assets load only on wp-login.php and any pages you opt into via the Integrations
tab. The JS check uses a tiny inline script with no external request. Geo lookups
from CDN headers are zero-latency. API fallback results are cached per IP for a 
configurable period. If you opt in to the early-block mu-plugin, it turns away locked-
out IPs before WordPress fully loads.

### What happens if the CAPTCHA API goes down?

All providers fail open. A network or API error never locks out legitimate users.

### Will this conflict with other security plugins?

It hooks specifically into WordPress login actions and standard WooCommerce and 
form-plugin hooks. If another plugin hooks the same actions it may conflict. Open
a support thread with details if you see an issue.

### Can I use this without any CAPTCHA?

Yes. Set the provider to None. Geo-blocking, brute-force lockout, honeypot username
trapping, the JS presence check, and 2FA all work independently of CAPTCHA.

### Does it work with WooCommerce?

Yes. The WooCommerce My Account login, registration, lost-password, checkout, and
order-tracking forms each have their own toggle in the WooCommerce section of the
Integrations tab.

### I locked myself out. How do I get back in?

Two options. First, add `define( 'NAHNLS_BYPASS_IP', 'your.ip.address' );` to wp-
config.php (above the “That’s all, stop editing” line). It accepts a comma-separated
list of IPs and CIDR ranges, and exempts them from lockouts, the IP blocklist, geo
blocking and the early-block rate limit – no deactivation needed. Second, rename
the plugin folder from `nahnu-login-shield` to `_nahnu-login-shield` via SFTP, which
deactivates it. If you are stuck at the two-factor step instead, see the next question.

### Can I put my own username on the honeypot list?

Yes, and it is a good idea: usernames are public (author pages, feeds, the REST 
API), so bots try them. Add your username to the Honeypot list on the Lockout tab
and sign in with your email address from then on. Anyone who tries the username 
is locked out like a bot. Email addresses of real accounts are never blocked, so
patterns like `admin@*` do not affect your own sign-in. The Lockout tab lists which
entries are real accounts and reminds you if your own username is on the list. If
you get locked out anyway, add `define( 'NAHNLS_DISABLE_HONEYPOT', true );` to wp-
config.php (above the “That’s all, stop editing” line) to switch the honeypot list
off, sign in, fix the list, then remove the line. To clear an IP that is already
locked, also see the question above.

### I can’t receive the 2FA email / I’m locked out

When two-factor authentication is on, every sign-in asks for a code, and email codes
are the baseline. If the email does not arrive (a mail problem on the server, spam
filtering), first try a backup code or “Email me a code” again. If that does not
help, add `define( 'NAHNLS_DISABLE_2FA', true );` to wp-config.php (above the “That’s
all, stop editing” line). This switches the 2FA check off for everyone, so you can
sign in with just your password; the plugin’s settings page shows a notice while
it is set. Fix email delivery (an SMTP plugin usually does it), add an authenticator
app on your Login & security page so you are not dependent on email, then remove
the line. Note: `NAHNLS_BYPASS_IP` does NOT bypass 2FA – it only exempts addresses
from lockouts, the IP blocklist, geo blocking and the early-block rate limit.

### Can my login, registration and My Account pages be cached?

Mostly yes. Every protected form carries a hidden security token that lives about
a day. On page load the plugin’s small script fetches a fresh token (one request
to admin-ajax.php), so pages served from a full-page cache keep working as long 
as JavaScript runs – including cached blog posts with a comment form. Without JavaScript,
a cached copy of the form older than a day fails the security check and asks the
visitor to reload. Login, registration and My Account pages should still be excluded
from full-page caching (WooCommerce requires this anyway). Developers can lengthen
the token life with the `nahnls_form_nonce_life` filter.

### Is this compatible with multisite?

It is not tested on multisite and is not recommended for network activation.

### How does geo-blocking work when I do not use a CDN?

It falls back to one of two free IP geolocation APIs (freeipapi.com or ipinfo.io)
with a configurable cache. CDN headers are always checked first and take priority.

### How do I get a new form plugin supported?

Open a support thread or submit a request at https://nahnuloginshield.com/support/.
Bricks Builder, SureForms and WordPress comments are the supported integrations 
today; anything else can be wired up through the developer API (docs/API.md) while
a request is considered.

### What is the mu-plugin and is it safe to have?

The mu-plugin is optional and opt-in. Nothing is copied until you, as an administrator,
click **Install early-block mu-plugin** on the General tab. That button copies `
nahnls-early-block.php` from the plugin folder into your must-use plugins directory(`
WPMU_PLUGIN_DIR`, normally `wp-content/mu-plugins/`). The file checks whether the
requesting IP is locked out or rate-limited and, if so, sends a 403/429 response
before WordPress fully loads — dramatically reducing server load during bot floods.
The General tab shows whether it is installed, not installed or outdated; after 
a plugin update you are offered an **Update mu-plugin** button (it is never refreshed
automatically). Clicking **Remove** deletes the file, and it is also removed on 
plugin deactivation and deletion. If the directory is not writable, the settings
page shows manual copy instructions instead.

The per-IP rate-limit counters the mu-plugin uses are tiny text files stored inside
your WordPress uploads directory at `wp-content/uploads/nahnu-login-shield/ratelimit/`(
protected by an `index.php` and `.htaccess`; files older than five minutes are pruned
automatically). Nothing is written outside the WordPress installation; the folder
is deleted when the plugin is uninstalled.

### Can attackers bypass the lockout by rotating IPs?

The per-username lockout catches this: if the same username is attempted from too
many different IPs within the time window, the username itself is locked regardless
of IP. Adding common targets (admin, administrator, root) to the Honeypot username
list gives the strongest protection — one attempt from any IP is enough to lock 
both the IP and the username globally.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“Nahnu Login Shield” is open source software. The following people have contributed
to this plugin.

Contributors

 *   [ Jaime Alnassim ](https://profiles.wordpress.org/ja1me4/)

[Translate “Nahnu Login Shield” into your language.](https://translate.wordpress.org/projects/wp-plugins/nahnu-login-shield)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/nahnu-login-shield/),
check out the [SVN repository](https://plugins.svn.wordpress.org/nahnu-login-shield/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/nahnu-login-shield/)
by [RSS](https://plugins.trac.wordpress.org/log/nahnu-login-shield/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.0.11

 * Changed: real usernames on the honeypot list are blocked again. Usernames are
   public, so you can put your own on the list and sign in with your email address
   instead; bots that try the username are locked out. Email addresses of real accounts
   are never treated as honeypots. The Lockout tab shows which entries are real 
   accounts and warns you when your own username is on the list.
 * New: `define( 'NAHNLS_DISABLE_HONEYPOT', true );` in wp-config.php switches the
   honeypot username list off (emergency recovery, see FAQ).
 * Fixed: SureForms. The widget was added by a browser-side scan that looked for
   a submit button SureForms 2.x no longer marks as type=”submit”, so nothing was
   ever injected and the check could not pass. The JS marker and CAPTCHA are now
   printed by the server inside each SureForms form (above the submit button, or
   after the fields for inline buttons).
 * New: protect all SureForms forms or only the ones you tick (Integrations tab,
   shown while the SureForms integration is on).
 * Fixed: with reCAPTCHA v3 or invisible Turnstile, a SureForms form could not submit;
   inside SureForms forms the token is now kept fresh in the form at all times.
 * Fixed: the hidden form nonce was tied to the logged-in user, so a logged-in administrator
   testing a form that submits over the REST API (SureForms) always got “Security
   check expired”. The nonce now depends on the site and time only.

#### 1.0.10

 * New: white-label emails. Settings > Email branding sets the sender name and address,
   a logo from the media library, accent and header colours, footer text and the
   subject / heading / intro (with placeholders) of the 2FA code, magic link and
   signup confirmation emails, which now share one template. Includes a browser 
   preview and a “Send test email” button. The “Nahnu Shield” badge and “Sent by
   Nahnu Login Shield” footer are gone; an opt-in “Protected by Nahnu Login Shield”
   line is available (off by default). New filters nahnls_email_args and nahnls_email_html.
 * Changed: two-factor authentication now protects everyone it applies to. With 
   2FA on, every user in the roles selected under “Apply to roles” (all roles by
   default; roles added later are covered too) is asked for a code on every password
   login. Email codes are always on as the baseline; users can add an authenticator
   app or passkey for stronger protection, and the code is emailed automatically
   only to users with nothing stronger. Users can no longer turn 2FA off for themselves;
   administrators can “Reset 2FA” for another user (falls back to email codes). 
   The Remind / Enforce mode is gone: sites that used Enforce keep their selected
   roles, every other site now applies 2FA to all roles. New wp-config.php recovery
   switch NAHNLS_DISABLE_2FA (see FAQ).
 * Security: every form the plugin protects (wp-login.php, WooCommerce, comments,
   SureForms, Bricks, nahnls_widget_html()) now carries a hidden form nonce that
   is verified before any protection field is read; the 2FA challenge form, the 
   emailed magic-link and signup-verification links and the 2FA challenge URLs carry
   their own nonces, verified before the token or key is read. Developers rendering
   their own CAPTCHA markup must echo nahnls_form_nonce_field() (see docs/API.md).
   The nonce is refreshed by JavaScript on page load, so cached pages keep working(
   see FAQ).
 * Fixed: a honeypot entry that is also a real account (the default list includes
   admin, support, info@* and admin@*) locked that account’s owner out for 24 hours
   as soon as they signed in with it. Real usernames and emails are no longer treated
   as honeypots; the Lockout tab shows which entries are ignored for that reason,
   and “Add to honeypot” in the log refuses real accounts.
 * Fixed: with REST protection on, every anonymous REST request that carried an 
   Authorization header WordPress does not understand (Bearer / JWT tokens from 
   other plugins, server-level HTTP auth) counted as a failed login, so five ordinary
   API calls could lock out an IP. Only credentials WordPress actually rejected 
   count now.
 * Fixed: on servers with APCu, the failed-attempt counter was not reset by a successful
   login.
 * Changed: the signup confirmation email’s subject, heading and message are edited
   on the Access tab, directly below the signup verification settings, with their
   own Preview and “Send test” buttons. Sender, logo and colours stay under Settings
   > Email branding, which now holds the two-factor and magic-link wording.
 * Fixed: on the Edge Blocking tab, the status badge could say “Not configured” 
   right after saving a key that was saved fine (it reflected a momentary API check
   instead of the saved settings) and then “Active” after a reload. The badge now
   shows the saved state, with a new “Key saved, choose a zone” step; the API check
   result is shown under the key field.
 * Improved: the “Active IP lockouts” table has a “Triggered by” column (form or
   API, number of failed attempts, username, time) and marks your own IP.
 * Changed: the username lockout and honeypot-username checks on wp-login.php now
   run from the wp_authenticate action with the username WordPress hands over, instead
   of reading the posted form on login_init.
 * Changed: plugin prefix renamed from the three-letter “n”+”ls” to “nahnls” to 
   meet the WordPress.org prefix guideline. Functions, classes, options, the log
   table, user meta, cron hooks, shortcodes, hooks, the REST namespace (now nahnls/
   v1) and JavaScript globals (window.NAHNLSAuth etc.) all use the new prefix. A
   one-time migration runs automatically on upgrade and carries over settings, the
   event log, user 2FA/passkey data and cron schedules. The wp-config constant is
   now NAHNLS_BYPASS_IP (the old name is still honoured). Developers integrating
   via docs/API.md must update their hook, shortcode, REST and JS names.
 * Removed: the server-side handlers for ~40 form, membership, LMS and community
   plugins that were present in the code but not exposed in the UI. The supported
   integrations are Bricks Builder, SureForms and WordPress comments; everything
   else integrates through the developer API.
 * Changed: the early-block mu-plugin is now strictly opt-in. It is no longer copied
   into the must-use plugins directory on activation, update or settings-page load;
   install, update or remove it with the explicit buttons on the General tab. Existing
   installs: the old copy is removed during migration, so click “Install early-block
   mu-plugin” if you want it back.
 * Changed: the mu-plugin’s per-IP rate-limit counters now live in wp-content/uploads/
   nahnu-login-shield/ratelimit/ (with index.php and .htaccess) instead of the system
   temp directory, and are removed on uninstall.
 * Changed: every script and style is a static file loaded with wp_enqueue_script/
   wp_enqueue_style; dynamic values are passed with wp_localize_script. The plugin
   prints no inline in the injector; the JS-presence marker is now set on injected
   forms and the OOPSpam widget initialises on them.
 * Fixed: Unchecked toggles in the Forms tab could inherit their previously saved
   value.
 * Performance: removed per-request opcache invalidation (it was disabling opcache
   for the whole plugin); now runs once on activation/update.
 * Performance: WAF pushes (Bunny/Cloudflare) and notification emails on lockouts
   and honeypot hits now run asynchronously via WP-Cron instead of inside the attacker’s
   request.
 * Performance: widget CSS/JS and CAPTCHA provider scripts are only enqueued on 
   pages that actually render a protected form.
 * Performance: added (event, created_at) index to the log table; schema version
   1.1.
 * Performance: DB-version check and mu-plugin install no longer run on every front-
   end request.
 * Performance: analytics queries and Chart.js only load on the Analytics tab and
   are cached for 5 minutes; username-lockout scan only on the Lockout tab.
 * Performance: log table geo/lockout lookups batched into a single query; fixed
   a geo cache key that never matched.
 * Performance: idle-timeout activity write throttled to once per minute.
 * Fixed: dashboard widget and CSV export were silently capped at 50 rows; MainWP
   stats queried a non-existent table.

#### 1.0.3

 * Fixed: WooCommerce checkout captcha showing when “Protect WC checkout” toggle
   is off.
 * Fixed: WC checkout/order-tracking only active when another WC option was also
   enabled.
 * Fixed: OOPSpam widget CSS/JS not enqueued on the WC checkout page (assets were
   only loaded on My Account pages).

#### 1.0.2

 * Security hardening: json_validate() guards on all external API responses.
 * Fixed void function return type error in nahnls_ajax_bunny_fetch_zones().
 * Fixed parse errors caused by PHP string escaping in settings and form-protector
   classes.
 * Fixed multi-value match arm syntax for broader PHP 8.x compatibility.

#### 1.0.1

 * Minor stability and compatibility fixes.

#### 1.0.0

 * Initial public release.
 * Brute-force lockout with escalation, per-username lockout, and honeypot usernames.
 * CAPTCHA providers: OOPSpam, reCAPTCHA v2/v3, hCaptcha, Cloudflare Turnstile (
   managed/invisible), Friendly Captcha.
 * Two-factor authentication (TOTP) with per-role enforcement.
 * Geo-blocking via CDN headers (Cloudflare, Bunny, CloudFront, Vercel, Fastly) 
   with IP API fallback.
 * Edge WAF integration for Bunny.net and Cloudflare with auto-expiry and block 
   log.
 * JavaScript presence check.
 * Custom login URL with configurable 404/redirect behaviour.
 * REST API protection: username enumeration block, lockout enforcement, Application
   Password toggle.
 * Session security: idle timeout with warning dialog, force-logout all sessions.
 * Login notifications: per-login email, admin alerts, new-country alerts, weekly
   digest.
 * WooCommerce My Account integration: login, registration, lost-password, checkout,
   order tracking.
 * Form protection framework with CAPTCHA + JS marker for form plugins and WooCommerce.
 * Full event log with search, filter, date range, CSV export, and source-form detail
   column.
 * Analytics: daily chart, time-of-day heatmap, event breakdown, top IPs, top usernames.
 * Early-block mu-plugin: locked-out IPs blocked before WordPress loads.
 * APCu support for attempt counters (100x faster than DB transients on supported
   hosts).
 * MainWP extension companion plugin available separately.

## Meta

 *  Version **1.0.11**
 *  Last updated **3 hours ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 6.2 or higher **
 *  Tested up to **7.1.3**
 *  PHP version ** 8.3 or higher **
 * Tags
 * [Brute Force](https://wordpress.org/plugins/tags/brute-force/)[captcha](https://wordpress.org/plugins/tags/captcha/)
   [geo blocking](https://wordpress.org/plugins/tags/geo-blocking/)[login security](https://wordpress.org/plugins/tags/login-security/)
   [two factor](https://wordpress.org/plugins/tags/two-factor/)
 *  [Advanced View](https://wordpress.org/plugins/nahnu-login-shield/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/nahnu-login-shield/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/nahnu-login-shield/reviews/)

## Contributors

 *   [ Jaime Alnassim ](https://profiles.wordpress.org/ja1me4/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/nahnu-login-shield/)