# WordPress user enumeration: what it reveals and how to stop it

WordPress user enumeration explained: where usernames leak, why they matter less than you think, and how to stop user enumeration without breaking your site.

By BetterShield Team, Oct 11, 2026. Updated Nov 8, 2026 by Nazmul Hasan Rupok.

WordPress user enumeration means asking a site, one automated request at a time, for the usernames of its accounts. A default WordPress install answers in more places than most owners expect: an API that lists authors, a short link that redirects to an author's page, a sitemap, embeds, and a sign-in form that says whether the name you typed exists.

None of that lets anyone in by itself. A username is half of a sign-in, and the half that was never meant to be secret. Still, a list of real names is a head start for anyone guessing passwords. This guide covers where the names leak, how much that matters, how to stop user enumeration in WordPress by hand, and how BetterShield's **Username privacy** changes six supported discovery routes, with one switch.

## Quick summary

- WordPress hands out usernames through the REST API's user list, ?author= links, the users sitemap, embeds, and the different answers its sign-in and lost-password forms give.
- A username keeps nobody out. Strong passwords, sign-in lockouts and two-factor do the real work; hiding names takes away a ready-made list of targets.
- Closing each route by hand takes code in several places, and the lost-password form is the hardest to get right.
- BetterShield's **Username privacy** changes six supported routes: five for signed-out visitors, and the users sitemap for everyone.
- Watch for author directories that read the public user list, author links on plain permalinks, and mistyped reset addresses.

## Where WordPress user enumeration happens

On a default install, anyone can ask:

| Route | What a stranger asks for | What WordPress answers |
|---|---|---|
| The REST API | /wp-json/wp/v2/users | Public author entries exposed by that route, including their slugs |
| Author links by number | /?author=1, /?author=2 and so on | A redirect to that author's archive, whose address carries the slug |
| The users sitemap | /wp-sitemap-users-1.xml | Every author's archive |
| Embeds | The embed details of any public post | The author's name and archive link |
| The sign-in form | A made-up username, then a real one with a wrong password | "is not registered on this site" for the first, and that the password was incorrect for the second |
| The lost-password form | An address with no account | "There is no account with that username or email address." |

The slug is the part of an author's address after /author/. WordPress makes it from the username when the account is created, so it usually matches the username.

The two forms matter most, because they have to stay open for real people. A script sends a list of likely names and sorts them by the answer it gets back.

## Do usernames matter? Less than people think

A username was never a secret. WordPress accepts an email address at sign-in as well, and many themes print each author's name and archive link on every post. If a password is strong and unique, knowing the name in front of it gets nobody in.

So why close the routes at all?

- **It gives a guesser confirmed targets.** With confirmed names, every guess goes to an account that exists, such as an administrator's.
- **Different answers sort guesses.** A form that says "wrong username" one time and "wrong password" the next tells a script which half it got right.
- **It costs most sites nothing.** Signed-in work does not need these routes to be public.

BetterShield's audit rates it accordingly. **Anyone can see your usernames** is a **Low** finding, explained as: "The REST API lists user profiles to visitors by default, which hands automated tools a list of usernames to try passwords against."

Hiding usernames is a small step. Take it after the big ones, not instead of them.

## Check your own site

Open a private window, so you are signed out, and try each route:

1. **/wp-json/wp/v2/users** (or /?rest_route=/wp/v2/users without pretty permalinks). A list with "slug" fields means the route is open.
2. **/?author=1**. If the address bar ends at /author/something/, that something is a slug.
3. **/wp-sitemap-users-1.xml**. A list of author addresses means the sitemap lists them.
4. **The sign-in form.** Try a made-up username, then a real one with a wrong password. Two different messages mean the form tells them apart.
5. **The lost-password form.** Try an address that has no account. An error saying so means the form tells them apart too.

Also open a post. If the author's name links to /author/..., your theme publishes that slug, whatever else you close.

## How to stop user enumeration in WordPress by hand

Each route has its own fix, and each fix has a catch:

- **The REST API.** Remove the user list for signed-out visitors only, with a few lines of code in a small plugin. Signed-in requests still need it: the block editor uses it to list authors.
- **?author= links.** Send numeric ?author= requests from signed-out visitors somewhere else. A server rule that refuses the parameter everywhere also catches the dashboard's Posts screen, which filters by author with the same parameter.
- **The sitemap.** WordPress's own sitemap can leave out its users list with code. If your sitemap comes from another plugin, check what it lists.
- **Embeds.** Code can strip the author from the embed details of your posts.
- **The sign-in form.** Code can replace WordPress's two messages with one, as long as it leaves alone the messages people need, such as a lockout notice.
- **The lost-password form.** Changing the message is not enough. WordPress sends a real account to a "check your email" page and keeps an unknown one on the form with an error, so both cases have to end in the same place.

Each piece is small, but six pieces of custom code are six things to keep working through WordPress updates.

## Stop it with BetterShield: Hide usernames

**Username privacy** is under **What the site reveals** on **BetterShield › Protection › Hardening**. In its own words, it "closes the routes that hand a visitor your usernames, including the sign-in form."

1. Press **Preview the change**. It lists every change and says "This is a preview. Nothing was changed and nothing was written."
2. Turn the switch on. The row shows **On since** and **Undo never expires**, and the activity log records "Hardening applied".
3. On **Findings**, **Anyone can see your usernames** closes.

For visitors who are not signed in, it changes six answers:

- **The REST API:** /wp-json/wp/v2/users, and a single user by number, answer 404.
- **?author= with a number:** the visitor is sent to the home page. An author archive reached at its own address still works, because that address already shows the slug.
- **The users sitemap:** it leaves the sitemap index, and a direct request for it answers 404. Posts and pages stay in the sitemap. If an SEO plugin serves its own sitemaps, its author sitemap answers 404 as well, and the author lists it builds into other sitemaps are emptied; its sitemap index may go on naming that address until author sitemaps are switched off in the SEO plugin too.
- **Embeds:** an embed of your post no longer names its author.
- **The sign-in form:** a wrong password and an unknown username both get "The username or password you entered is incorrect." BetterShield's own messages, such as a sign-in pause, are unchanged, and the form still shakes.
- **The lost-password form:** a username or address with no account is sent to the same "check your email" page a real request reaches, and no email is sent.

That last one has a cost, and the fix names it: "Somebody who mistypes their address is told a link is on its way and no email arrives, which is the trade this makes."

The block editor and the Users screen work as before, because only the users sitemap changes for signed-in people, and the fix writes no files.

Quick Setup offers the same fix as **Stop username discovery**. From the terminal, `wp bettershield harden user_enumeration --user=admin` applies it and `--undo` reverses it. The full reference is the [Hide usernames](/docs/block-user-listing/) guide.

## What it can break

- **Author directories that read the public user list.** The finding's own note: "Could break: nothing on most sites. Sites embedding author directories from the public API would need the block removed."
- **Author links on plain permalinks.** Without pretty permalinks, WordPress links each author's archive as ?author= with a number, so those links take signed-out visitors to the home page.
- **A mistyped reset address.** The person is told a link is coming and none arrives. If someone says their reset email never came, check the address they typed.
- **Author pages in the sitemap.** Author archives leave WordPress's built-in sitemap and an SEO plugin's author sitemap, though they still exist and posts still link to them.

To undo, turn the switch off. Every answer goes back to WordPress's own at once, and "Hardening undone" is logged.

## Pair it with the protections that matter more

Hidden names help most alongside the protections that stop guessing itself:

- **Sign-in lockouts.** By default, 5 wrong passwords within 15 minutes pause sign-in from that connection for 15 minutes, and an unknown username counts as a failure. See [Limit login attempts in WordPress](/blog/limit-login-attempts-wordpress/).
- **Two-factor for the roles that can change the site.** A correct password is then not enough. See [WordPress two-factor authentication](/blog/wordpress-two-factor-authentication/).
- **No account named "admin".** The audit flags **A user is named “admin”** (Low), because nobody needs enumeration to guess that one.
- **Publish from an account that cannot manage the site.** Your theme still shows author names and links, so keep administrator usernames off public pages.
- **Password reset limits.** **Slow repeated password reset requests**, on by default, answers a connection that keeps asking the way a successful request is answered, without sending anything.

For the rest of the hardening fixes, see [WordPress hardening: 30 one-click fixes](/blog/wordpress-hardening/).

## Test discovery and compatibility separately

Use a private browser to check the supported public routes, but keep the test small. Repeatedly submitting wrong passwords or reset requests can trigger limits and send unwanted emails. Use a disposable account you control and an invented name, with a known recovery path available.

After applying the setting, verify both sides:

- **Discovery behavior:** the supported public user routes return the intended refusal or redirect, and the supported sign-in/reset messages no longer distinguish the test names.
- **Publishing behavior:** the editor can still select authors and publish, and your public bylines and intended author archives remain usable.
- **Directory behavior:** any team directory or headless front end that reads the public user API still meets its requirements.
- **Caching:** the CDN and page cache are not serving an older user list or form response.

**Worked example (fictional):** a magazine intentionally publishes author biographies, but does not need an unrestricted public user-list API. It can keep those biographies while limiting supported enumeration routes. A membership site whose front end builds a directory directly from that API needs a different decision or a deliberately designed public directory endpoint.

A public name, a user slug and a login name can differ. Record what each test actually reveals instead of labeling every returned name a leaked login credential. The setting reduces supported bulk-discovery routes; it does not promise anonymity for people who publish on the site.

## Common mistakes

- **Treating hidden usernames as protection.** They slow nothing down on their own. Lockouts and two-factor do.
- **Turning off the whole REST API for everyone.** The block editor uses it, and so may other plugins on your site. Only the user list needs closing, and only for signed-out visitors.
- **Closing the API and forgetting the forms.** The sign-in and lost-password forms still sort real names from guesses until their answers match.

## Frequently asked questions

### What is user enumeration in WordPress?

Collecting a site's usernames from outside: from the REST API's user list, ?author= links, the users sitemap, embeds, or the sign-in and lost-password forms answering real and made-up names differently.

### Is WordPress user enumeration a security risk?

A small one. A username is not a password, and BetterShield's audit rates a public user list **Low**. It can give password guessing tools a list of targets, so close it, but put strong passwords, lockouts and two-factor first.

### Will blocking user enumeration break my site?

On most sites, no. Signed-in requests are untouched. What changes is anything that reads authors from the public API, author links without pretty permalinks, and what a mistyped address sees on the lost-password form.

### Why does the reset form say a link is coming for an address with no account?

So the form cannot be used to test addresses. A real account and an unknown one reach the same page; only the real one gets an email. The cost is that someone who mistypes their own address is not told.

### Does stopping user enumeration stop brute-force sign-in attempts?

No. Scripts can still guess common names and email addresses. Limits slow attempts from counted connections; two-factor reduces what a guessed password alone can do.

## Conclusion

WordPress user enumeration is real, and it matters less than its reputation suggests. Closing it takes one more advantage away from anyone guessing passwords, and with BetterShield it takes one switch that leaves signed-in work alone. Do it after lockouts and two-factor, not instead of them.

BetterShield is free on [WordPress.org](https://wordpress.org/plugins/bettershield/). Every detail of the fix is in the [Hide usernames](/docs/block-user-listing/) guide.
