# WordPress login security: the layers in order, lockouts to passkeys

Secure the WordPress login in a sensible order: recovery first, then lockouts, the bot check, two-factor, passkeys and an optional new sign-in address.

By M Asif Rahman, Nov 1, 2026. Updated Nov 8, 2026.

WordPress login security is a set of layers, and the order you add them in matters: **BetterShield** puts your way back in first, then limits password guessing, then asks for a second factor, then lets people skip the password with a passkey. This guide walks the layers in that order, says what each one does, and points to the post that goes deep on it.

Each layer has its own guide, so this post is the map and not the manual. If you only want the lockout numbers, [Limit login attempts in WordPress without locking yourself out](/blog/limit-login-attempts-wordpress/) covers them. If you want the second factor, start with [WordPress two-factor authentication](/blog/wordpress-two-factor-authentication/).

## Quick summary

- Set up recovery before anything that can lock a person out. A recovery link and recovery codes start safe mode for an hour.
- **Sign-in attempt limits** is on by default: 5 failures in 15 minutes lock out the connection for 15 minutes. The account is never locked.
- **Block simple bots on forms** is on by default and asks people for nothing. It does not stop a targeted attacker.
- Two-factor with an authenticator app and passkeys are per account. Require them of the roles that can change the site.
- Moving the sign-in page makes the logs quieter, not the door stronger. Treat it as an extra.

## Why the order matters

The sign-in form is the one door every WordPress site has, and a script can try it all day. The [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html) lists the same ideas in general terms: limit repeated guesses, ask for more than a password, and keep a safe way to recover an account.

Each layer you add can also stop the owner. A lockout can catch your own typos, and a required second factor can strand someone without a phone. That is why the order below starts with the way back in. BetterShield enforces it too: before it applies a sign-in change, it checks that you hold a working recovery link or an unused recovery code, checked within the last day.

| Order | Layer | On by default? | Where |
|---|---|---|---|
| 1 | Recovery link and codes | Link emailed at activation | **Protection › Recovery** |
| 2 | Lockouts | Yes | **Protection › Login & Access** |
| 3 | Hidden bot check | Yes | **Protection › Login & Access** |
| 4 | Allow and block lists | Empty | **Protection › Login & Access** |
| 5 | Two-factor with an app | Per account | **Protection › Two-Factor** |
| 6 | Passkeys | Per account | **Protection › Two-Factor** |
| 7 | A new sign-in address | Off | **Protection › Login & Access** |

## Layer 1: your way back in

Open **BetterShield › Protection › Recovery**. BetterShield gives you two ways back in that need no password: a recovery link and eight single-use recovery codes. Either one turns on safe mode for an hour, which pauses everything on **Login & Access** while it lasts.

1. Press **Generate Recovery Link** if you have no link you can find. A new link retires the earlier one and is shown once.
2. Press **Issue Recovery Codes** and print or copy the eight codes. Keep them off the site.
3. Press **Check Recovery Options**. It checks the link, the codes, an administrator address and the email path without using any of them.

[Set up recovery](/docs/set-up-recovery/) has every option, and [Locked out](/docs/locked-out/) covers using them on the day. [Locked out of WordPress](/blog/locked-out-of-wordpress/) is the plain walkthrough.

## Layer 2: limit password guessing

**Sign-in attempt limits** is under **Sign-in protection** on **BetterShield › Protection › Login & Access**. A connection that reaches **Failures allowed** within **Counted within (minutes)** is locked out for **Lockout lasts (minutes)**.

| Option | Default |
|---|---|
| **Failures allowed** | 5 (range 3 to 100) |
| **Counted within (minutes)** | 15 |
| **Lockout lasts (minutes)** | 15, doubling for repeats within a day, up to a day |

The account is never locked, only the connection the failures came from, so its owner can still sign in from somewhere else. BetterShield automatically emails a lockout notice only when the account has used that network within the last 30 days, or its correct password was used. On a new network, choose **Email Me an Unlock Link** on the sign-in screen and enter your username or email address. A link is sent only when that lockout qualifies, to the address already saved on the account. Before you rely on this layer, check that **Visitor addresses** reads **Readable**. Behind a proxy or Cloudflare it may not, and [WordPress behind Cloudflare or a proxy](/blog/wordpress-real-visitor-ip-proxy/) fixes that.

There is also **Ask for a sign-in puzzle after wrong passwords**, off by default. It asks only after two wrong passwords from one connection, and it needs HTTPS and JavaScript. The full detail is in [Sign-in lockouts](/docs/sign-in-lockouts/).

## Layer 3: turn away simple bots

**Block simple bots on forms** adds a hidden field and a signed timestamp to the sign-in, registration and comment forms. People see nothing and solve nothing. A submission is refused when the hidden field is filled, the timestamp is wrong, or it arrives less than two seconds after the form was drawn. On the sign-in form it checks only when the password was right.

In the screen’s own words, it cuts bot noise and does not stop a targeted attacker. A form BetterShield did not draw, such as one a page builder made, is not checked. [Refuse automated sign-ins, sign-ups and comments](/docs/bot-check/) lists exactly what it covers, and [A WordPress login CAPTCHA alternative](/blog/wordpress-login-captcha-alternative/) explains why it asks people for nothing.

## Layer 4: lists for the people you know

Two lists sit under **Allow and block lists**, both empty by default. **Always allowed** holds addresses that are never locked out, such as an office with one shared address. **Never allowed to sign in** refuses sign-in to an address, a username or a browser fragment you name.

Add an address to **Always allowed** only when you trust every request from it. The **Recent sign-in attempts** card shows the networks and names tried most, and **Block** beside a name that is not an account adds it to the block list at once. See [Allow and block lists](/docs/allow-and-block-lists/) and [See who is trying to sign in](/docs/recent-sign-in-attempts/).

## Layer 5: a second factor from an app

Two-factor asks for a six-digit code from an authenticator app after the password, so a stolen password alone signs nobody in. Set it up for yourself first on **Protection › Two-Factor**:

1. Press **Set up two-factor**.
2. Scan the QR code with an authenticator app, or type the **Manual entry key**.
3. Enter the code the app shows and press **Confirm code**.
4. Store the ten backup codes, then press **I have stored these codes**.

Nothing changes at sign-in until that last step. To ask others for it, **Require two-factor of a role** picks the roles and a grace period, 14 days by default. [Require two-factor](/docs/require-two-factor/) has the detail, and [Enforce 2FA in WordPress](/blog/enforce-two-factor-wordpress/) covers rolling it out to a team. Only app codes count toward the requirement. A passkey does not. If the code every morning is what people resent, [trusted browsers](/blog/wordpress-2fa-trusted-devices/) in BetterShield Ultra let a browser skip it for up to 90 days.

## Layer 6: passkeys

A passkey is a key your device keeps and unlocks with a fingerprint, face or PIN. The site stores only the public half, and the browser offers it only to this site’s own domain. Add one on the **Passkeys** card on **Protection › Two-Factor**: type a name in **Name this device**, press **Add a passkey**, prove it is you, and confirm on the device.

A passkey is an extra way in and does not replace the password. On an account with two-factor, a passkey unlocked by fingerprint, face or PIN signs in with no code asked, because it counts as both factors. It needs HTTPS, and every other sign-in rule, lockouts included, still applies. We recommend at least two passkeys per person, one per device. If you want to turn passwords off for a role, [Passkey-only sign-in](/blog/wordpress-passkey-only-sign-in/) walks through it, and [Passkeys](/docs/passkeys/) has the full guide.

## Layer 7: move the sign-in page, if you want to

**Custom sign-in address** moves the WordPress sign-in page from wp-login.php to an address you choose, under **Signing in** on **Login & Access**. It starts off. Requests to wp-login.php then answer 404.

The screen’s own words are the honest ones: it makes the logs quieter, not the door stronger. Anyone who learns the new address meets the same form. Use **Preview the change** and **Monitor first** before you enforce it, and apply **Dashboard privacy for visitors** too, or /wp-admin/ hands out the new address. [Change WordPress login URL](/blog/change-wordpress-login-url/) and the [Change the sign-in address guide](/docs/change-sign-in-address/) cover the trial.

## What it can break, and how to undo

- **An office behind one address** shares one lockout count. Add its fixed address to **Always allowed**, or press **Release Now** on the row in **Lockouts**.
- **A tool with an old password** can lock out its own server. Fix its credential, then press **Release Now**.
- **A password manager that auto-submits** within two seconds is refused by the bot check once. Pressing sign in again a moment later works.
- **A tool that signs in over XML-RPC or the REST API** with an account that has two-factor is refused. An application password skips the code.
- **A moved sign-in address** breaks old bookmarks and hand-typed links, which now answer 404.

Every layer here is a switch or a list entry, and turning it off puts the old behavior back. If you cannot sign in at all, use your recovery link, a recovery code, or `wp bettershield recover`. With **Ask for my password before removing a protection** on, removing a protection asks for your password.

## Check it is working

- **Login & Access at a glance:** **Visitor addresses** reads **Readable**, and the **Protections** tile counts what is on.
- **Lockouts:** after five wrong passwords on a test account from a phone, a row appears marked **Active** with **Release Now**.
- **The sign-in page source:** search for “Leave this field empty”, the hidden bot-check field.
- **Two-Factor:** the **Your two-factor** tile reads **On**, and a passkey shows its name with “last used” after a sign-in.
- **The activity log:** “Sign-in failed”, “Sign-in lockout started”, “Two-factor turned on” and “Signed in with a passkey”.

## Recommended settings

We recommend this order: generate a recovery link and codes and run **Check Recovery Options**; keep **Sign-in attempt limits** and **Block simple bots on forms** on; turn on the proof of work if the site is served over HTTPS; set up your own two-factor and add a passkey on each device you use; then require two-factor of the roles that can change the site. Treat the moved sign-in address as optional, last.

## Frequently asked questions

### Does BetterShield lock a user account after wrong passwords?

No. It locks out the connection that keeps failing, never the account. The account’s owner can still sign in from another connection, and can request an eligible unlock link through **Email Me an Unlock Link**. Automatic notices are limited to a network used within the last 30 days or a correct password being used.

### Do I need two-factor if I already have passkeys?

We recommend keeping both. A passkey unlocked by fingerprint, face or PIN signs in without a code, and two-factor covers the day you sign in from a device that does not hold your passkey.

### Will the bot check slow down real visitors?

No. It adds a hidden field and a signed timestamp, needs no JavaScript and no outside service, and shows people nothing. A submission arriving within two seconds of the form being drawn is refused once, and sending it again works.

### Should I change my wp-login.php address?

It is an extra, not a core layer. It stops scripts that ask for wp-login.php by name and does nothing against someone who learns the new address. Set up lockouts and two-factor first, and trial the move with **Monitor first**.

## Conclusion

Good WordPress login security is a short list done in order: a way back in, limits on guessing, a second factor, and passkeys for the people who want to skip the password. Each layer is a switch you can preview and undo. BetterShield is free on [WordPress.org](https://wordpress.org/plugins/bettershield/), and the lockout settings are in [Sign-in lockouts](/docs/sign-in-lockouts/).
