WordPress login error messages, explained becomes an important topic as soon as you look beyond the visible red box on wp-login.php.
A failed WordPress login can produce different errors depending on what actually went wrong.
For example, WordPress may distinguish between:
empty username
empty password
unknown username
unknown email address
incorrect password
authentication failure
That is useful for legitimate users because the message can tell them what to fix.
But detailed authentication errors can also reveal information to someone deliberately probing the login form.
For example:
Unknown username
→ this account probably does not exist
Incorrect password for john
→ john is probably a valid account
This creates a familiar security tradeoff:
better user feedback
vs.
less account information disclosure
WordPress does not treat every login failure as a single generic event. Its authentication system uses WP_Error objects with specific error codes, plugins can introduce additional authentication failures, and the final message shown on the login screen can be modified through filters.
This guide explains the default WordPress login errors, where they originate, what their error codes mean, how username and email authentication differ, why detailed messages can assist account enumeration, how to make login errors more generic when appropriate, what not to hide, and why changing the message is only one small part of a complete WordPress login hardening strategy.
Where WordPress login errors appear
The standard WordPress login page normally lives at:
https://example.com/wp-login.php
Visiting:
https://example.com/wp-admin/
while logged out generally sends the visitor to the same authentication flow.
The official WordPress Logging In documentation describes the standard login page and its Username or Email Address and Password fields.
When authentication fails, WordPress can return one or more errors that are then rendered above the login form.
WordPress authentication returns WP_User or WP_Error
The central WordPress authentication function is:
wp_authenticate()
The official wp_authenticate() documentation describes its return values as:
WP_User
→ authentication succeeded
WP_Error
→ authentication failed
This makes login failures structured application data rather than merely strings printed onto the page.
A WordPress login error has a code and a message
Conceptually, a login failure can look like:
Error code:
invalid_username
Error message:
The username is not registered.
or:
Error code:
incorrect_password
Error message:
The password is incorrect.
The error code allows WordPress, plugins and custom code to understand why authentication failed without parsing the human-readable message.
Common WordPress login error codes
Core authentication can produce codes including:
empty_username
empty_password
invalid_username
invalid_email
incorrect_password
authentication_failed
Other authentication systems and plugins can add their own codes.
This means you should not build custom login logic assuming that WordPress will only ever return two errors.
The empty username error
If the login identifier is empty, WordPress can produce:
empty_username
For username authentication, the current core message identifies the username field as empty.
For email authentication, WordPress also uses the historical empty_username code for compatibility even though the human-readable error can refer to the email field.
The current implementations are visible in the official wp_authenticate_username_password() and wp_authenticate_email_password() references.
The empty password error
If no password is submitted, WordPress returns:
empty_password
The current message is conceptually:
Error:
The password field is empty.
This error is mostly a usability aid.
It tells the legitimate user that authentication was never meaningfully attempted because the required password field was blank.
The invalid username error
If WordPress receives a username that does not correspond to a registered account, username authentication can return:
invalid_username
The current WordPress implementation is quite explicit.
It tells the visitor that the submitted username is not registered on the site and suggests trying the account email address instead.
This behavior can be inspected directly in wp_authenticate_username_password().
The invalid email error
If a syntactically valid email address is submitted but no WordPress account uses it, email authentication can return:
invalid_email
The current core message indicates that the email address is unknown and suggests trying the username.
The implementation is documented in wp_authenticate_email_password().
The incorrect password error
If WordPress identifies a valid account but the supplied password fails verification, authentication returns:
incorrect_password
Current WordPress messages identify the account value involved.
For username authentication, the message effectively says:
The password entered for this username is incorrect.
For email authentication, the equivalent message references the submitted email address.
Both versions can also include a link to the password-recovery flow.
This creates an account-enumeration difference
Compare these two responses:
Attempt 1
Username:
random-name-9283
Response:
Username is not registered
with:
Attempt 2
Username:
johnsmith
Response:
Password for johnsmith is incorrect
The second response suggests:
johnsmith exists
while the first suggests:
random-name-9283 does not exist
An automated system can repeat that process against many candidate identifiers.
This is one form of user enumeration.
For the wider set of disclosure paths, see How Attackers Collect WordPress Usernames and Email Addresses.
Enumeration is not account compromise
Discovering:
johnsmith
does not log the attacker into WordPress.
The attacker still needs to defeat the authentication controls protecting that account.
Those may include:
- a strong password;
- two-factor authentication;
- rate limiting;
- IP controls;
- other authentication restrictions.
Reducing account enumeration is therefore a supporting hardening measure rather than the primary security boundary.
This distinction is also important when reviewing WordPress REST API User Enumeration, Explained.
Why does WordPress provide detailed errors?
Because detailed messages are useful to real users.
Imagine a person enters:
john.smith@example.com
and WordPress responds only:
Login failed.
The user does not know whether:
- the email address is wrong;
- the password is wrong;
- the account does not exist;
- the site no longer accepts email login;
- an authentication plugin rejected the account.
A more detailed error reduces uncertainty.
Usability and disclosure pull in opposite directions
The design problem is therefore not:
detailed errors = bad
generic errors = good
It is:
How much information should an
unauthenticated visitor receive?
On a small internal WordPress site, detailed feedback may be perfectly reasonable.
On a public login page receiving constant automated probing, reducing unnecessary account disclosure may be worthwhile.
WordPress supports login by username and email
Another important detail is that standard WordPress authentication supports both:
username + password
and:
email + password
The two authentication paths are implemented separately.
WordPress attaches both:
wp_authenticate_username_password()
wp_authenticate_email_password()
to the authentication pipeline.
The official authenticate filter documentation shows these core callbacks.
For the wider security implications, see WordPress Login: Username vs. Email, Which Is More Secure?.
A failed login can therefore reveal either identifier type
Suppose an attacker tests:
ceo@example.com
If WordPress says:
Unknown email address
that suggests the address is not attached to a WordPress account.
If WordPress instead says:
The password entered for ceo@example.com is incorrect
the attacker has gained evidence that the email address corresponds to an account.
Public email addresses can make this especially relevant
Many business email addresses are intentionally public:
marketing@example.com
support@example.com
john@example.com
If those same addresses can authenticate privileged WordPress users, the login identifier may already be known before any probing begins.
For strategies around separating public and privileged identities, see How Attackers Collect WordPress Usernames and Email Addresses.
The authentication pipeline is filterable
WordPress does not hardcode authentication into one completely closed function.
Authentication passes through:
authenticate
which can return:
WP_User
WP_Error
null
The official authenticate hook documentation explains that plugins can introduce additional authentication and validation logic at this stage.
Plugins can therefore create their own login errors
A security plugin might return errors such as:
account_locked
ip_blocked
two_factor_required
account_disabled
A membership plugin might return:
membership_expired
A custom application might return:
account_pending_approval
These are examples, not WordPress core error codes.
The important point is that WP_Error allows authentication extensions to communicate specific failures.
wp_authenticate_user can reject a valid account too
After WordPress identifies a user but before password validation completes, authentication passes through:
wp_authenticate_user
The official wp_authenticate_user filter can return either a valid WP_User or a WP_Error.
This can be used for additional account policies.
Conceptually:
account exists
↓
additional policy check
↓
allowed?
yes → continue authentication
no → return WP_Error
Authentication errors are not limited to bad credentials
This distinction matters when debugging login problems.
A user may have:
correct username
+
correct password
but authentication can still fail because:
- the account has been blocked;
- a plugin applies an additional policy;
- two-factor authentication fails later;
- the account requires approval;
- an integration rejects the request.
Do not assume every login error means the password is wrong.
WordPress fires wp_login_failed after failed authentication
For most normal authentication failures, WordPress triggers:
wp_login_failed
The official wp_authenticate() implementation shows that this action receives:
$username
$error
where $error is the WP_Error describing the failure.
This makes the hook useful for:
- logging failed authentication;
- security monitoring;
- rate-limit systems;
- alerting;
- auditing custom authentication failures.
For the monitoring side, see How to Monitor WordPress Login Attempts.
Empty fields are treated differently by wp_login_failed
There is a subtle core behavior here.
wp_authenticate() excludes:
empty_username
empty_password
from the normal wp_login_failed action path.
This makes sense because submitting an incomplete form is somewhat different from an actual credential-validation failure.
The generic authentication_failed error
If all registered authentication handlers return neither a user nor a specific error, WordPress creates:
authentication_failed
with a generic message indicating that the username, email address or password is invalid.
This fallback is defined inside wp_authenticate().
The login screen has another error-filtering layer
After authentication has produced errors, WordPress still provides hooks for modifying what appears on the login screen.
Two important filters are:
wp_login_errors
login_errors
wp_login_errors filters the WP_Error object
The official wp_login_errors filter receives:
WP_Error $errors
string $redirect_to
This allows code to modify the structured error collection before it is displayed.
This is useful when you need to:
- remove one error;
- replace one message;
- add another login message;
- preserve specific error codes.
login_errors filters the rendered error string
WordPress also exposes:
login_errors
The official login_errors documentation describes it as the filter applied to the login error message printed on the login screen.
Conceptually:
WP_Error
↓
WordPress prepares HTML message
↓
login_errors
↓
browser
Which filter should you use?
If you want to preserve structured error information and selectively modify error codes, wp_login_errors is often the cleaner layer.
If the requirement is simply:
replace the final login error text
then login_errors can be sufficient.
Simple generic login-error example
A common hardening approach is to replace detailed authentication errors with one generic message.
For example:
add_filter(
'login_errors',
function ( $error ) {
return 'Login failed. Check your credentials and try again.';
}
);
This prevents the standard login page from directly distinguishing:
invalid username
invalid email
incorrect password
in the displayed text.
But replacing every login message blindly can be a bad idea
The login screen contains more than failed credential messages.
WordPress can display messages related to:
- password recovery;
- registration;
- logged-out state;
- password reset;
- other login actions.
If your filter converts absolutely every message into:
Login failed.
you can degrade legitimate workflows.
Target authentication failures specifically
A more careful implementation can inspect error codes rather than replacing every possible login-screen message indiscriminately.
For example:
add_filter(
'wp_login_errors',
function ( $errors ) {
$codes = $errors->get_error_codes();
$credential_errors = [
'invalid_username',
'invalid_email',
'incorrect_password',
'authentication_failed',
];
if (
array_intersect(
$credential_errors,
$codes
)
) {
foreach (
$credential_errors
as $code
) {
$errors->remove(
$code
);
}
$errors->add(
'authentication_failed',
'<strong>Error:</strong> Login failed. Check your credentials and try again.'
);
}
return $errors;
}
);
This preserves unrelated login-screen messages while normalizing credential failures.
Do not copy login filters without testing HTML escaping
WordPress login errors can contain HTML such as:
<strong>labels;- password-recovery links;
- plugin-provided markup.
If custom code accepts user-controlled values or constructs arbitrary markup, escape and sanitize appropriately.
A login error is still output on a public page.
Should login errors reveal the submitted username?
From a usability perspective, a message such as:
The password entered for johnsmith is incorrect.
confirms exactly which identifier WordPress attempted to authenticate.
That can help a real user notice:
johnsmth
instead of
johnsmith
From a security perspective, the same confirmation can help an automated probe distinguish valid accounts.
There is no universal answer for every site.
Generic login errors are most useful on public authentication surfaces
The argument for generic messages becomes stronger when:
wp-login.phpis publicly reachable;- the site receives frequent automated login traffic;
- privileged usernames are intentionally kept separate from public identities;
- the organization wants to reduce passive enumeration clues.
If username disclosure is part of your threat model, also review Why WordPress Author Archives Leak Usernames.
Generic login messages do not prevent username enumeration everywhere
This is extremely important.
You could change:
wp-login.php
to always show:
Login failed.
while the same site still exposes account identifiers through:
- author archives;
- REST API users;
- public author slugs;
- feeds;
- page metadata;
- plugins;
- custom APIs.
For the REST-specific path, see WordPress REST API User Enumeration, Explained.
Login-error hiding is defense in depth
The correct security model is:
reduce unnecessary login disclosure
+
reduce unnecessary username exposure
+
strong passwords
+
2FA
+
rate limiting
+
monitoring
not:
generic error message
=
secure WordPress login
Generic errors do not stop brute force
An attacker does not need a detailed error to continue guessing credentials.
A response such as:
Invalid credentials.
still tells the attacker:
authentication failed
which is enough to continue trying.
The rate of repeated authentication requests should be controlled separately.
See WordPress Brute-Force Attacks, Explained and How to Limit Login Attempts in WordPress.
Rate limiting solves a different problem
Generic login errors reduce information disclosure.
Rate limiting controls how many requests can be attempted.
The distinction is:
Generic errors
→ reduce feedback
Rate limiting
→ reduce attempt volume
Both can contribute to login hardening, but neither replaces the other.
Monitoring is another separate layer
You should also be able to answer:
How many failed logins occurred?
Which accounts were targeted?
Which IP addresses generated them?
When did the activity happen?
For that workflow, see How to Monitor WordPress Login Attempts.
Two-factor authentication reduces the value of correct passwords
If an attacker eventually obtains:
valid username
+
valid password
a second authentication factor can still prevent account access.
TheOneWP Two-Factor Authentication provides an additional TOTP authentication layer for supported WordPress login workflows.
This is a much stronger security boundary than hoping account identifiers remain secret forever.
Custom login URLs solve another problem
Changing the login endpoint can reduce automated noise directed specifically at:
/wp-login.php
but it does not make credentials stronger.
It also does not replace generic login errors, rate limiting or 2FA.
TheOneWP Custom Login URL handles that separate layer.
Do not assume a hidden login URL is secret forever
A custom login URL can be discovered through:
- browser history;
- shared documentation;
- redirect behavior;
- plugins;
- links;
- configuration leaks.
Use it to reduce opportunistic traffic, not as the primary authentication secret.
Account blocking can create custom errors too
Sometimes the credentials are valid but the account itself should no longer authenticate.
For example:
Former contractor
↓
account record retained
↓
login disabled
TheOneWP Block User Login can prevent selected accounts from authenticating while preserving the user record.
This distinction also appears in How to Audit User Roles on a WordPress Site.
Password recovery has its own errors
The login form is not the only place where WordPress can reveal whether an account exists.
The lost-password workflow also accepts:
username
or
email address
The relevant core function is:
retrieve_password()
The official retrieve_password() documentation shows errors such as:
empty_username
invalid_email
invalidcombo
Password recovery can also reveal account existence
The current WordPress recovery logic can return a message indicating that no account exists for a supplied username or email address.
This means changing only normal login errors can leave another enumeration path untouched.
A security audit should therefore review:
login
+
lost password
+
registration
+
REST API
+
author archives
+
custom account endpoints
Do not break password recovery while hiding account existence
Password recovery has to remain usable for legitimate users.
A generic recovery workflow might say:
If an account exists for the information provided,
a password-reset message will be sent.
This reduces explicit disclosure while still giving legitimate users a reasonable next step.
But if you modify the native flow, test actual email delivery carefully.
The reset email itself should only go to real accounts
A generic browser message does not mean WordPress should send an email to an arbitrary address.
The application still needs to:
look up user
↓
verify account exists
↓
generate reset key
↓
send email
The generic response changes only what the unauthenticated visitor learns.
Login errors and password-recovery errors are separate filters
WordPress provides:
lostpassword_errors
for password-recovery failures.
The current hook is documented inside retrieve_password().
Do not assume login_errors controls the entire account-recovery system.
WordPress also applies a shake effect to certain errors
The standard login form can visually shake when particular errors occur.
Current WordPress core includes codes such as:
empty_password
empty_email
invalid_email
invalidcombo
empty_username
invalid_username
incorrect_password
retrieve_password_email_failure
in the default shake-error list.
The implementation can be inspected in the official login_header() source.
The shake effect is presentation, not authentication
Changing:
shake_error_codes
does not change whether login succeeds.
It changes whether the login form receives that visual animation.
This is another useful reminder that:
authentication logic
and
login-page presentation
are separate layers.
Custom login pages must preserve useful error states
Branding the WordPress login screen can improve client experience, but a custom design still needs to communicate failures clearly.
Useful errors should remain:
- visible;
- readable;
- close to the form;
- accessible to assistive technologies;
- consistent with the site’s authentication policy.
For the broader design workflow, see How to Customize the WordPress Login Page.
Do not hide every authentication failure visually
A login form that simply reloads with no explanation creates a terrible user experience.
The user needs at least to understand:
Authentication did not succeed.
A reasonable generic message could be:
Login failed. Check your username or email address and password, then try again.
This communicates failure without confirming which credential was valid.
Do not use vague server-style errors for normal login failures
A message such as:
Error 403
or:
Authentication exception
is technically meaningful to developers but unhelpful to most users.
Good generic messages should still provide a useful action:
Check credentials
Try again
Recover password if necessary
Do not reveal internal implementation details
Custom authentication code should avoid errors such as:
SQL lookup returned no rows for wp_users.user_login
or:
TOTP verification exception in Vendor\Plugin\Auth
Internal details belong in logs, not on the public login page.
User-facing errors and technical logs serve different audiences
A useful architecture separates:
USER MESSAGE
Login failed.
Check your credentials and try again.
from:
SECURITY LOG
2026-09-09 01:12:03
identifier: johnsmith
error_code: incorrect_password
source_ip: ...
user_agent: ...
The user needs a safe, understandable response.
The administrator may need detailed diagnostic information.
Do not log raw passwords
This should never be necessary.
A login-security log can record:
- time;
- submitted identifier where appropriate;
- error code;
- IP-related data according to privacy policy;
- user agent;
- request context.
It should not record:
plaintext password
for failed authentication attempts.
Error-message hardening does not replace privacy review
If authentication logs contain:
- usernames;
- email addresses;
- IP addresses;
- timestamps;
- user-agent information;
they may contain personal data.
Define:
- why the data is collected;
- how long it is retained;
- who can access it;
- how it is protected.
Custom authentication integrations may bypass wp-login.php
A WordPress site can authenticate through more than the standard interactive login form.
Examples include:
- XML-RPC;
- Application Passwords;
- REST integrations;
- WooCommerce customer login;
- membership plugins;
- SSO;
- custom frontend forms.
Changing login_errors affects the normal login-screen output.
It does not automatically normalize errors generated through every other authentication channel.
Application Password errors are separate
WordPress Application Passwords have their own authentication mechanism and errors.
The official wp_authenticate_application_password() documentation includes failures involving:
- unknown username or email;
- Application Passwords being unavailable;
- Application Passwords being unavailable for the user;
- invalid Application Password credentials.
Do not assume login-page error filters protect API authentication surfaces.
XML-RPC is another authentication surface
WordPress XML-RPC can authenticate requests independently of a browser visiting wp-login.php.
If the site does not need it, review the implications separately in What Is XML-RPC in WordPress, and Why Disable It?.
Changing interactive login messages does not disable XML-RPC authentication.
WooCommerce can have a frontend login form
WooCommerce My Account can provide customer login and registration interfaces outside the normal visual wp-login.php screen.
See WooCommerce Account and Checkout Pages, Explained for the account-page architecture.
If your security requirement is:
all public login errors must be generic
test WooCommerce separately.
Membership plugins need separate testing too
A membership site may implement:
/login/
/member-login/
/account/
/signin/
using its own authentication UI.
The plugin may:
- call normal WordPress authentication internally;
- replace the displayed error messages;
- implement its own account lookup;
- introduce additional validation.
Do not assume one core filter changes every frontend login form.
Generic messages can complicate support
Suppose a user repeatedly receives:
Login failed.
The actual problem may be:
wrong email address
but the support team cannot distinguish that from:
wrong password
from the user’s screenshot alone.
That is the cost of reducing information disclosure.
Provide a clear password-recovery path
If login errors are generic, make sure users can easily recover access.
WordPress exposes:
wp_lostpassword_url()
for generating the password-recovery URL.
The official wp_lostpassword_url() documentation describes this helper.
A login interface might therefore show:
Login failed.
Check your credentials and try again.
Forgot your password?
Do not remove password recovery just to reduce enumeration
Preventing legitimate users from recovering access can produce more operational risk than the enumeration reduction is worth.
The better goal is:
usable recovery
+
limited disclosure
rather than:
no recovery
=
security
Login errors should not expose whether an account is privileged
A custom authentication system should never say something like:
Administrator account found,
but password incorrect.
or:
This email belongs to an Editor.
Role and capability information is unnecessary for an unauthenticated user.
For the permission model, see WordPress User Roles and Capabilities, Explained.
Do not include database IDs in public login errors
Likewise, avoid:
User ID 17 exists but authentication failed.
A database identifier provides no useful recovery information to the person trying to log in and unnecessarily exposes internal account information.
Should you completely hide username validity?
For many public-facing sites, using a generic credential-failure message is a reasonable defense-in-depth measure.
For example:
Invalid username
↓
generic failure
Invalid email
↓
generic failure
Wrong password
↓
generic failure
becomes:
Authentication failed.
Please check your credentials.
That makes simple login-based enumeration less informative.
But assume usernames can still become known
Your login-security architecture should remain safe even if an attacker knows:
administrator username
The main defenses should remain:
- strong unique passwords;
- two-factor authentication;
- rate limiting;
- login monitoring;
- least privilege;
- secure recovery.
This is why username secrecy should never become the foundation of the site’s security model.
Review author archives separately
If public author pages are unnecessary, they may create another account-discovery path.
Depending on the site configuration, consider whether the archive itself is useful.
TheOneWP Disable Author Archive can remove the public author archive workflow when it serves no useful purpose.
Author slugs and login usernames are not always identical
WordPress stores:
user_login
user_nicename
display_name
user_email
as separate account values.
The author archive normally uses user_nicename, not directly the raw login field.
However, on many sites the nicename and username begin with the same value, which can still create useful correlation.
TheOneWP Hide Author Slug can decouple the publicly visible author slug from the login-related identifier.
REST API user enumeration needs its own protection
A generic login error does nothing to a request such as:
/wp-json/wp/v2/users
if that endpoint exposes users under the site’s current permissions and publishing state.
See:
- WordPress REST API User Enumeration, Explained;
- How to Restrict the WordPress REST API;
- WordPress REST API Security Basics.
Do not disable the REST API just because login errors reveal usernames
These are separate systems.
Modern WordPress and many plugins depend on REST functionality.
The better approach is to address each disclosure path according to its actual requirements instead of disabling unrelated APIs globally.
Test the exact WordPress error codes
When customizing login behavior, test at least:
blank username
blank password
unknown username
unknown email
known username + wrong password
known email + wrong password
valid login
Do not test only one incorrect password and assume the entire error system behaves the same way.
Test password recovery separately
Also test:
blank recovery identifier
unknown username
unknown email
valid username
valid email
reset email delivery
reset link
new password form
Login and password recovery share account information but are different workflows.
Test plugins that modify authentication
If the site uses:
- two-factor authentication;
- CAPTCHA;
- login attempt limits;
- account approval;
- SSO;
- WooCommerce;
- membership software;
verify how each system interacts with custom error handling.
Test blocked accounts
If the site can disable a user’s login, check what message appears.
The user should receive a useful response without exposing more account information than necessary.
Test rate-limit errors
A rate-limit system should not necessarily return the same response as a normal bad password.
A legitimate user may need to know:
Too many login attempts.
Try again later.
That message does not reveal whether the original username or password was correct.
429 can be appropriate for deliberate rate limiting
At the HTTP layer, a deliberate request-rate limit can use:
429 Too Many Requests
when appropriate.
That is separate from the normal human-readable WordPress login message.
Test two-factor errors separately
Two-factor authentication happens after the primary credentials are accepted in many implementations.
That creates another state:
username valid
password valid
second factor invalid
Do not collapse this into:
wrong username or password
if doing so makes legitimate recovery impossible.
Users need to understand whether they are at the password step or the second-factor step.
Generic primary-credential errors and specific 2FA errors can coexist
For example:
Primary credentials failed:
Login failed. Check your credentials.
Primary credentials succeeded,
TOTP failed:
The verification code is invalid or expired.
The second message does not reveal a username to someone who has not already passed the first authentication stage.
Test custom login-page branding with errors visible
A login design that looks perfect before submission can break when a long error message appears.
Test:
- short errors;
- long errors;
- multiple errors;
- password-reset messages;
- mobile layouts;
- translated strings.
For login-page UX, see Branding the WordPress Login Screen for Clients.
Translations can change message length substantially
Do not design the error container around one English sentence.
A translated version may be considerably longer.
Allow login errors to:
- wrap naturally;
- expand vertically;
- remain readable;
- avoid overlapping the form.
Accessibility matters for login errors
An authentication failure should not be communicated only through:
red color
shake animation
The message itself needs readable text.
Users relying on assistive technologies need access to the same failure information as sighted users.
Do not remove error text and keep only the shake
The standard WordPress shake is a visual cue.
It is not a complete accessible error message.
Keep meaningful text even if you customize its wording.
How to audit WordPress login error disclosure
- Open the standard login page while logged out.
- Submit an empty username.
- Record the error code and visible message.
- Submit an empty password.
- Submit a clearly nonexistent username.
- Submit a clearly nonexistent email address.
- Submit a known username with the wrong password.
- Submit a known email with the wrong password.
- Compare the visible responses.
- Test the lost-password page.
- Test an unknown recovery username.
- Test an unknown recovery email.
- Test REST API user exposure separately.
- Check author archives.
- Check custom frontend login forms.
- Check WooCommerce My Account if installed.
- Check membership authentication if installed.
- Check two-factor failures.
- Check account-blocking messages.
- Check rate-limit messages.
A simple login-error exposure matrix
| Attempt | Default information | Possible disclosure |
|---|---|---|
| Empty username | Required field missing | Minimal |
| Empty password | Required field missing | Minimal |
| Unknown username | Username not registered | Account absence |
| Unknown email | Email unknown | Account absence |
| Known username + wrong password | Password incorrect for username | Account existence |
| Known email + wrong password | Password incorrect for email | Account existence |
Common WordPress login-error mistakes
Assuming all failed logins return the same error
WordPress uses multiple error codes.
Assuming login errors are only strings
Authentication uses structured WP_Error objects.
Replacing every login message globally
You can accidentally hide registration or recovery messages.
Making the error completely invisible
Users still need to understand authentication failed.
Returning internal exception details
Technical diagnostics belong in logs.
Logging failed passwords
There is no good reason to store plaintext password attempts.
Believing generic messages stop brute force
They reduce disclosure, not request volume.
Believing generic messages stop all username enumeration
Author archives, REST endpoints and plugins can expose accounts separately.
Disabling password recovery to hide users
This damages legitimate account recovery.
Ignoring email-based enumeration
WordPress accepts both usernames and emails by default.
Testing only wp-login.php
WooCommerce, membership plugins and APIs may have separate authentication UIs.
Making 2FA failures indistinguishable from password failures
Users need meaningful feedback after they have reached the second-factor stage.
Relying on username secrecy
Account identifiers should be assumed discoverable eventually.
WordPress login error checklist
- Know that WordPress authentication returns
WP_UserorWP_Error. - Understand common core error codes.
- Test
empty_username. - Test
empty_password. - Test
invalid_username. - Test
invalid_email. - Test
incorrect_password. - Understand the fallback
authentication_failederror. - Remember that WordPress accepts username or email by default.
- Review whether detailed messages expose account existence.
- Decide whether generic credential errors are appropriate.
- Use
wp_login_errorswhen structured error modification is useful. - Use
login_errorscarefully when modifying final output. - Do not replace unrelated login messages accidentally.
- Keep login failures visible and understandable.
- Provide a clear password-recovery path.
- Review
lostpassword_errorsseparately. - Test unknown usernames during recovery.
- Test unknown emails during recovery.
- Do not expose roles in authentication errors.
- Do not expose user IDs in authentication errors.
- Do not expose stack traces or internal implementation details.
- Do not log raw passwords.
- Monitor failed login attempts.
- Rate-limit repeated authentication attempts.
- Use strong unique passwords.
- Use two-factor authentication for privileged accounts where appropriate.
- Review public author identifiers.
- Review REST API user exposure.
- Review custom login forms.
- Review WooCommerce login where applicable.
- Review membership login where applicable.
- Review XML-RPC authentication requirements.
- Review Application Password authentication separately.
- Test account-blocking messages.
- Test rate-limit messages.
- Test two-factor failures.
- Test login errors on mobile.
- Test translated messages.
- Test accessibility.
- Perform changes on staging first.
- Keep a working administrative session available while modifying authentication.
- Document custom login filters.
Related guides
- A WordPress Login Hardening Checklist
- WordPress Login: Username vs. Email, Which Is More Secure?
- How Attackers Collect WordPress Usernames and Email Addresses
- WordPress Brute-Force Attacks, Explained
- How to Limit Login Attempts in WordPress
- How to Monitor WordPress Login Attempts
Final recommendation
WordPress login errors are more than decorative messages above wp-login.php.
They are the visible output of a structured authentication system built around:
identifier
↓
authentication callbacks
↓
WP_User or WP_Error
↓
error codes
↓
login-page filters
↓
user-facing message
Current WordPress core can distinguish between conditions such as:
empty username
empty password
unknown username
unknown email
incorrect password
generic authentication failure
That specificity is useful for legitimate users, but the difference between:
username does not exist
and:
password for this username is incorrect
can also help an unauthenticated requester determine which accounts exist.
For public WordPress login surfaces, using a generic primary-credential failure such as:
Login failed.
Check your credentials and try again.
can therefore be a reasonable defense-in-depth measure.
But it should remain exactly that: one layer.
A stronger WordPress authentication architecture looks like:
limited unnecessary identifier exposure
+
generic credential failures where appropriate
+
strong unique passwords
+
two-factor authentication
+
rate limiting
+
failed-login monitoring
+
secure password recovery
+
least privilege
Changing one login sentence does not stop brute force, does not disable REST API user enumeration, does not remove public author information, does not protect a reused password and does not prevent authentication through another enabled endpoint.
Use the official wp_authenticate(), wp_authenticate_username_password(), wp_authenticate_email_password(), wp_login_errors and login_errors references when modifying the native behavior.
Then review the rest of the authentication surface instead of assuming the red error box is the whole security model.

