
WordPress login security matters more than almost anything else you can do for your site, because wp-login.php is the most attacked URL on every WordPress installation on the internet. Not the popular ones, not the shops – every single one. Point a fresh domain at a fresh WordPress install and within 48 hours automated bots will be trying passwords against it. I have cleaned up enough hacked sites over 12 years to tell you that a weak login is still one of the two most common ways in, alongside outdated plugins.
The good news is that locking the login down properly is neither expensive nor complicated – and most of it is free. The trap is doing it carelessly and locking yourself out, which I have also seen plenty of times: a client with 2FA on a phone that fell in a lake, an agency with a changed login URL nobody wrote down, a lockout rule that banned the site owner’s own office IP. This guide covers the whole stack – passwords, two-factor authentication, rate limiting, login URLs, application passwords, server-level protection, sessions, usernames, recovery planning, team policy and monitoring – with the honest trade-offs of each layer, and with an escape route planned before every door you lock.
Table of contents
- What attacks on your login actually look like
- Passwords that survive: length beats complexity
- Two-factor authentication on WordPress: the honest comparison
- Limiting login attempts and rate limiting
- Changing the login URL: what it is really worth
- Application passwords for integrations
- Protecting wp-login and xmlrpc at server and WAF level
- Session management: log out everywhere
- Admin username hygiene
- Recovery planning: do not lock yourself out
- Team policies: roles, shared logins and offboarding
- Monitoring failed logins without drowning in noise
- Frequently asked questions
What attacks on your login actually look like
It helps to know what you are defending against, because the two main attack types need slightly different answers. The first is brute force at scale: botnets that walk through lists of common passwords against common usernames, thousands of sites at a time. They are not targeting you; they are targeting everyone. In an access log it looks like this (simplified):
185.220.x.x - POST /wp-login.php - 200 - "admin"
185.220.x.x - POST /wp-login.php - 200 - "admin"
103.145.x.x - POST /xmlrpc.php - 200
91.240.x.x - POST /wp-login.php - 200 - "vishal"
91.240.x.x - POST /wp-login.php - 200 - "webmaster"
Note two things. The requests come from many different IPs – modern botnets rotate addresses, so banning a single IP achieves little. And xmlrpc.php appears alongside wp-login.php, because XML-RPC allows authentication attempts too, and older attack tools can batch hundreds of password guesses into a single request through it.
The second type is credential stuffing: attackers take username and password pairs leaked from other services – and there have been billions of leaked pairs over the years – and try them against your login. This one defeats “strong” passwords entirely if the password was reused. A client’s store I cleaned up had a genuinely strong password: twelve characters, mixed case, symbols. It was also the same password the owner used on a forum that had been breached three years earlier. The attacker logged in on the first try, no brute force needed, and no failed-login limiter would ever have fired.
Those two attack types define the defence: unique passwords kill credential stuffing, rate limiting and 2FA kill brute force, and server-level rules cut the noise for everything else. Everything in this guide maps back to one of those three jobs. If your site has already been breached through the login, deal with that first – I wrote a full hacked WordPress site recovery guide – and then come back here to make sure it does not happen again.
Passwords that survive: length beats complexity
Password advice has been oversold and over-complicated for years, so here is the version that actually holds up:
- Unique beats everything. A password used on exactly one site cannot be stuffed from a breach elsewhere. This single property matters more than any complexity rule.
- Length beats complexity. A 20-character passphrase of plain words is vastly harder to brute force than “P@ssw0rd1!” – and far easier to type on a phone. Modern cracking rigs chew through short complex passwords; they choke on length.
- Use a password manager. 1Password, Bitwarden, or even the manager built into your browser. The manager generates a unique 20+ character password per site and remembers it so you do not have to. Every argument against managers falls apart against the reality of what people do without them: reuse.
- Use the WordPress generator. When WordPress suggests a password on the profile screen, take it and store it in the manager. It is long, random and unique – exactly right.
- Never email passwords. Send credentials through a password manager share or a one-time secret link, not in a message that sits in two inboxes forever.
For teams, you can enforce this rather than hope for it. Plugins like Password Policy Manager or the policy features inside larger security plugins let you set a minimum length (I set 16 for administrators, 12 for everyone else), force resets for accounts flagged in known breaches, and stop users from choosing passwords that appear in common-password lists. That last check matters most: the brute force lists that bots run are built from real leaked passwords, so blocking the top hundred thousand of them shuts out most automated guessing on its own.
One honest caveat: forced password rotation every 60 or 90 days – a policy some corporate clients ask me for – is counterproductive and no longer recommended by NIST or the UK’s NCSC. People respond to forced rotation by choosing weaker passwords with a counter on the end. Change passwords when there is a reason: a breach, a departure, a suspicion. Otherwise leave a long unique password alone.
Two-factor authentication on WordPress: the honest comparison
Two-factor authentication (2FA) means a login needs something you know (the password) plus something you have (a phone, a hardware key, a mailbox). It is the single biggest upgrade on this list, because it makes a stolen or guessed password insufficient on its own. Credential stuffing, phishing, a password written on a sticky note – 2FA stops the login anyway in most cases. If I could only make one change to a client’s site, this would be it.
Not all second factors are equal, though, and this is where I see bad choices made:
- TOTP authenticator apps (Google Authenticator, Authy, 1Password’s built-in TOTP) generate a six-digit code that changes every 30 seconds. Free, works offline, phishing-resistant enough for most threat models, and supported by every serious WordPress 2FA plugin. This is my default recommendation for every client.
- Passkeys / hardware keys (WebAuthn – a YubiKey or the passkey stored in your phone or password manager) are the strongest option: they are bound to your site’s domain, so a phishing page on a lookalike domain gets nothing. Support in WordPress plugins has matured well; the practical limitation is user familiarity and device management across a team.
- Email codes send a one-time code to the account’s email address. Better than nothing, and useful as a fallback, but the second factor is only as strong as the mailbox – and if the mailbox password was in the same breach, it is not a second factor at all. Also fragile: if your site’s email delivery breaks, so does your login.
- SMS codes are the weakest common option. SIM-swap attacks are real, SMS delivery is unreliable across countries, and most WordPress SMS 2FA plugins need a paid gateway anyway. I do not set SMS up for clients any more.
| Method | Security | Cost | Works offline | Honest verdict |
|---|---|---|---|---|
| TOTP app | High | Free | Yes | The default – use this |
| Passkey / hardware key | Highest | Free (or ~EUR 25 per key) | Yes | Best for admins, phishing-proof |
| Email code | Medium | Free | No | Fallback only, not the primary |
| SMS code | Low-medium | Gateway fees | No | Skip it |
On plugins: the free Two-Factor plugin (maintained by WordPress core contributors) covers TOTP, email and backup codes cleanly with no upsells. WP 2FA adds the policy layer – forcing 2FA for chosen roles with a grace period, which is exactly what teams need. Wordfence and Solid Security include solid 2FA modules if you already run them; do not install a second security plugin just for 2FA. Whatever you pick, the policy matters more than the plugin: 2FA must be mandatory for every administrator and editor, not optional. Optional 2FA protects the people who least need it – the careful ones – and leaves exactly the accounts an attacker wants. WP 2FA’s forced-setup-with-grace-period flow handles the rollout politely: users get a week to set it up, then cannot log in without it.

Limiting login attempts and rate limiting
Out of the box, WordPress allows unlimited login attempts – a bot can try passwords all day. A login limiter changes that: after a set number of failures, the IP (or the username) is blocked for a period. Limit Login Attempts Reloaded is the standalone standard and the free version is enough; Wordfence, Solid Security and most other security plugins include the same feature, and your host may implement it at server level – check before stacking two limiters on top of each other.
My settings, tuned over many client sites to balance defence against locking out real people:
- 4 allowed attempts, then a 20-minute lockout. Enough for a human who fumbles a password, useless for a bot.
- Escalating lockouts: after 3 or 4 lockouts from the same IP, extend the block to 24 hours.
- Trusted IP allowlist: add your own office or home IP so a colleague’s typos never lock the whole team out. This one setting prevents most of the support tickets limiters generate.
- Block on invalid usernames instantly if the plugin supports it – nobody legitimate tries to log in as “admin” or “test” on your site.
Be honest with yourself about what rate limiting achieves against modern botnets: an attacker rotating through thousands of IPs gets four free guesses per IP, so a limiter alone does not stop a distributed attack – that is what unique passwords and 2FA are for. What the limiter does brilliantly is kill the cheap, lazy majority of attacks, cut the CPU load that thousands of login POSTs put on shared hosting (I have seen brute force traffic alone make a site sluggish), and generate the failed-login data your monitoring feeds on. It is a necessary layer, not a sufficient one.
Changing the login URL: what it is really worth
Plugins like WPS Hide Login move your login page from /wp-login.php to something like /client-portal-x7/, returning a 404 on the default URL. You will find people who call this essential and people who call it pointless security theatre. Both overstate it; here is the honest middle.
What it does not do: stop a determined attacker. The login endpoint is discoverable in various ways, obscurity is not access control, and anyone who has your changed URL (a departed contractor, a leaked bookmark) walks straight past it. If your passwords are weak, a hidden login URL will not save you. This is why it is nowhere near the top of this guide.
What it does do: eliminate 95%+ of the automated noise, because botnets overwhelmingly hammer the default URLs and move on when they 404. On client sites where I have changed the URL, failed login attempts drop from hundreds per day to nearly zero. That means cleaner logs (real attacks stand out instead of drowning in noise), less server load, and fewer lockout emails. Reduction of noise is a genuine operational benefit even when the security benefit is small.
The caveats, learned the annoying way: write the new URL down somewhere the whole team can find it (a password manager entry is perfect), because “what was our login URL?” emails are real; exclude the new URL from your page cache; test that password-protected posts, registration and password reset still work, since badly built plugins can break those flows; and if any third-party service posts to wp-login.php or expects the standard flow, check it still functions. Worth doing on most sites – as the last layer, not the first.
Application passwords for integrations
Since version 5.6, WordPress has built-in application passwords: per-service credentials for the REST API, generated under Users > Profile. If any external service connects to your site – a mobile app, Zapier or Make, a deployment tool, an external uptime or SEO tool, custom code calling the REST API – it should authenticate with an application password, never with your real one.
The reasons are practical. Each application password is long, random and shown once; it can be revoked individually without touching your main login; the profile screen shows when each one was last used, which tells you what is actually still connected; and an application password cannot be used to log in to wp-admin interactively – it only works for API authentication, which limits the damage if one leaks. When I build API integrations for clients, each integration gets its own application password on a dedicated user with only the capabilities the integration needs – so the connection for an inventory sync cannot edit themes, and revoking the sync does not break anything else.
Two pieces of hygiene: first, audit the list twice a year and revoke anything unused – stale credentials for services you stopped using in 2024 are pure liability. Second, if you do not use the REST API for authenticated external access at all, some security plugins let you disable application passwords entirely; that is a reasonable hardening step on a simple brochure site. And note that 2FA does not apply to application passwords – the credential itself is the factor – which is exactly why each one must be unique per service and revocable.
Protecting wp-login and xmlrpc at server and WAF level
Everything so far runs inside WordPress, which means PHP boots for every attack request. The strongest and cheapest defence layer sits in front of WordPress, blocking hostile traffic before it costs you anything. Three options, from simplest to most involved:
- Cloudflare (free plan is enough). A WAF rule that challenges or rate-limits POST requests to /wp-login.php does most of the work: humans pass the managed challenge without noticing, bots fail it and never reach your server. On the free plan you can also rate-limit the login path and block countries you genuinely never log in from. This is what I set up for most client sites – ten minutes of work, permanent benefit.
- Server rules (.htaccess or nginx). If the people who log in have fixed IPs, an IP allowlist on wp-login.php is close to bulletproof: everyone else gets a 403 that costs the server almost nothing. Where IPs change, HTTP basic auth in front of wp-login.php adds a second, WordPress-independent password gate that automated tools do not handle. Crude, unfashionable and extremely effective.
- fail2ban (VPS users). Reads the auth log and bans IPs at the firewall after repeated failures – the same idea as a login limiter plugin, but enforced before PHP runs, at any scale of attack.
Then there is xmlrpc.php, the forgotten login door. XML-RPC is the legacy API that predates the REST API; its system.multicall method lets an attacker try hundreds of passwords in one HTTP request, quietly defeating naive attempt limiters. Almost nothing modern needs XML-RPC – the main exceptions are the Jetpack plugin and the classic mobile/desktop publishing apps. Check whether anything you run uses it (your logs will tell you); if not, block it at the server or via Cloudflare. If Jetpack needs it, restrict it to Automattic’s published IP ranges rather than leaving it open to the world. On the hacked sites I clean, an open xmlrpc.php with a weak password is a depressingly regular finding – it is one of the common WordPress security mistakes that gets sites compromised while the owner believes the login is protected.
Layered together, the picture looks like this: the WAF absorbs the flood, server rules guard the doors, the attempt limiter catches what slips through, 2FA makes a correct password insufficient, and the unique password makes guessing hopeless in the first place. No single layer is perfect; the stack is what makes an attacker move on to an easier site – and the internet is full of easier sites.

Session management: log out everywhere
A login is not just an event – it creates a session that lives on in cookies, and stolen or forgotten sessions are logins that already happened. WordPress has decent session tools that almost nobody uses. On every user’s profile screen there is a “Log Out Everywhere Else” button that destroys all other sessions for that account. Use it whenever a device is lost, a laptop is sold or repaired, you have logged in on a machine you do not control (hotel, client office, print shop), or you have any suspicion at all. After a password change, WordPress invalidates other sessions for that account – one more reason a password reset is the correct first response to anything suspicious.
Session length is the other lever. The “Remember Me” checkbox extends a login cookie from 48 hours to 14 days – fine on your own laptop, wrong on anything shared. For sites with real stakes (WooCommerce admins, membership sites, anything with personal data), I shorten session lifetimes with a small filter on auth_cookie_expiration or an inactivity-logout plugin such as Inactive Logout, so that an unattended wp-admin tab in an open-plan office does not stay live all week. Administrators can also review and destroy other users’ sessions – security plugins and WP 2FA expose this – which turns “I think Maria’s old laptop still has a session” from a worry into a two-click fix. None of this is glamorous, but session hygiene is what stops yesterday’s access from becoming today’s problem.
Admin username hygiene
Every login attempt needs a username and a password, and WordPress gives usernames away more easily than people realise. The classics: “admin” as a username (still the single most-guessed name in every brute force list – if your admin account is called admin, half the attacker’s work is done); author archive URLs like /?author=1 redirecting to /author/username/, which enumerates usernames account by account; the REST API’s users endpoint listing usernames of published authors; and themes printing the username as the visible author name.
The fixes are quick. Never create an account called admin, administrator, or the site’s own name – and if you have one, create a fresh administrator with a non-obvious username, log in as it, and delete the old account, assigning its content to the new one (WordPress prompts you; do not skip it or you delete the posts too). Set every user’s display name to something different from their username, so the byline shown on posts is not the login name. Block author enumeration – most security plugins have a toggle for the ?author= redirect, and restricting the REST users endpoint for logged-out visitors is a standard hardening step. And avoid using the public contact email address as the login identifier where you can, since WordPress accepts email logins and info@yourcompany.com is printed on your own website.
Honest framing: username secrecy is a minor layer – the security of your login must never depend on the username being unknown, because you should assume it leaks eventually. But there is no reason to hand over half the credential pair for free, and cutting username enumeration also degrades the quality of targeted attacks against specific accounts. Two minutes of hygiene, permanent small gain.
Recovery planning: do not lock yourself out
Here is the section most guides skip, and the one that generates the most panicked emails to me: every lock you add is also a lock that can catch you. A phone reset without 2FA backup codes saved, a changed login URL nobody recorded, a lockout rule triggered by your own hotel wifi, an email-code 2FA on a site whose email delivery just broke – I have untangled all of these for clients. Plan the recovery before you need it:
- Save 2FA backup codes the moment you enable 2FA. Every decent plugin generates a set of one-time recovery codes. Store them in the password manager next to the password. This is non-negotiable – it is the difference between a 30-second recovery and an afternoon of phpMyAdmin.
- Register two devices or methods where possible: a TOTP app plus a passkey, or TOTP stored in a password manager that syncs across devices, so one lost phone is an inconvenience rather than a lockout.
- Keep a second administrator account with its own 2FA, held by a second trusted person (or by your developer under agreement). One account having a problem should never mean zero admin access.
- Document the login URL, the limiter settings and the allowlisted IPs somewhere the whole team can reach – a shared password manager vault note is ideal.
- Know your break-glass route: with hosting-panel or SFTP access you can always get back in – deactivate the 2FA or limiter plugin by renaming its folder, or reset a password via WP-CLI or the database. Which means hosting access must be guarded at least as strongly as the site itself, with its own 2FA – it is the master key.
A useful discipline: after setting up any new lock, deliberately test the recovery path once – use a backup code, log in from the second account, find the documented URL. Five minutes of rehearsal beats discovering the plan’s hole during a real lockout on a launch day.
Team policies: roles, shared logins and offboarding
On sites with more than one user, the biggest login risks are organisational, not technical. Three policies fix most of it.
One person, one account. Shared logins (“everyone uses the marketing account”) mean no accountability in logs, a password that must be shared insecurely and can never be rotated without coordinating everyone, and no way to remove one person’s access when they leave. Accounts are free; create one per human, every time, including for your agency or freelancer.
Least privilege. Most users do not need to be administrators – an editor can manage all content, an author their own posts, a shop manager a WooCommerce store, none of them able to install plugins or create users. Every unnecessary administrator multiplies your attack surface: it is one more account whose compromise means total compromise. My rule on client sites is two administrators – the owner and one backup – and everyone else gets the lowest role that lets them do their actual job. When I clean hacked sites, finding five or eight administrators is the norm, and it always makes the forensics and the cleanup worse.
Offboarding on the day. When someone leaves the company or a contractor finishes, their account is deactivated or deleted (reassigning content), their sessions are destroyed, any application passwords they created are revoked, and any shared secrets they had access to – hosting login, the changed login URL – are rotated. Do it the same day, as a checklist item in the leaving process, not “when we get round to it”. Dormant accounts of departed people, still carrying editor or admin rights, are among the most common entry points I see, because nobody watches them and nobody misses them. I keep this in the quarterly review of my WordPress maintenance checklist: list every account, ask “does this human still need this access?”, and act on the answers.
Monitoring failed logins without drowning in noise
The last layer is knowing what is happening. Every limiter and security plugin can log failed logins; the skill is tuning what you look at, because raw failed-login data on a normal site is 99% meaningless botnet noise, and monitoring that cries wolf gets ignored – which is worse than no monitoring at all.
What is normal: a steady background of failed attempts against admin, test, and your domain name as a username, from IPs all over the world, at all hours. That is the weather. What deserves attention: failed attempts against real usernames that are not publicly guessable (someone has done reconnaissance); a sudden spike of ten or a hundred times the usual volume (your site is on a fresh target list, or someone is interested in you specifically); failures followed by a success from the same IP (check that login immediately); and any successful administrator login from an unfamiliar country or at an odd hour.
Configure alerts on exactly those events and nothing else. My standard setup for client sites: email or Slack notification on every administrator login (a low-traffic event worth seeing), on lockout escalations, and on logins from new devices or locations where the plugin supports it – Wordfence, WP Activity Log and Solid Security all cover most of this. A proper activity log plugin adds who-did-what auditing (user created, role changed, plugin installed), which is invaluable in the aftermath of any incident. Then put a monthly five-minute review in the calendar: skim the log summary, check for the four patterns above, check the user list for strangers. On the maintenance plans I run for clients this review is baked in – monitoring only works when a human actually looks at it on a schedule.
Want your login locked down properly – without locking yourself out?
Login hardening is part of every WordPress maintenance and security plan I run: 2FA rollout with recovery codes, rate limiting, WAF rules, user audits and monitoring, set up once and reviewed on a schedule – for small businesses and agencies in Germany, the UK, the USA and Australia. See my portfolio or tell me about your site – I reply within 24 hours with an honest assessment and a fixed quote.
Frequently asked questions
What is WordPress login security?
WordPress login security is the set of defences protecting your site’s login – strong unique passwords, two-factor authentication, login attempt limits, server-level rules on wp-login.php and xmlrpc.php, and monitoring. Layered together, they stop both brute force and credential stuffing attacks.
Is 2FA really necessary for a small WordPress site?
Yes – attacks are automated and hit every site regardless of size. A free TOTP app plus a free plugin takes ten minutes to set up and makes a stolen or guessed password insufficient on its own.
What is the best 2FA method for WordPress?
A TOTP authenticator app is the best default: free, offline and widely supported. Passkeys or hardware keys are even stronger for administrators. Email codes are a fallback only, and SMS is best avoided.
Does changing the WordPress login URL improve security?
Modestly. It will not stop a determined attacker, but it eliminates most automated bot noise, which means cleaner logs and less server load. Do it as a final layer, and document the new URL.
How do I stop brute force attacks on wp-login.php?
Combine layers: a login attempt limiter (around 4 attempts, escalating lockouts), a Cloudflare or WAF challenge on login POSTs, blocking or restricting xmlrpc.php, unique passwords and mandatory 2FA for privileged accounts.
What happens if I get locked out of my WordPress admin?
With hosting or SFTP access you can always recover: rename the security plugin’s folder to deactivate it, or reset a password via WP-CLI or the database. Prevent the drama by saving 2FA backup codes and keeping a second administrator account.
Should I disable xmlrpc.php on my WordPress site?
If nothing you use depends on it – Jetpack and legacy publishing apps are the main exceptions – yes. XML-RPC allows batched password guessing that bypasses naive login limiters, and blocking it closes a door most sites never use.