WordPress Security

Stopping WordPress User Enumeration Through the REST API, Author Archives and Sitemaps

Stopping WordPress User Enumeration Through the REST API, Author Archives and Sitemaps

Try this on your own site. Open a browser and visit your domain followed by /wp-json/wp/v2/users. On a default WordPress installation, you’ll see a neat JSON list of every user who has published content, including their user ID, display name and a field called “slug”. In many cases that slug is exactly the username they log in with. That’s WordPress user enumeration in two clicks.

Now try your domain followed by /?author=1. WordPress will probably redirect you to /author/something/, and that “something” is again the user’s slug.

This is called user enumeration, and it’s one of the first things automated attack tools do. Once they know valid usernames, they only need to guess passwords. That cuts the work of a brute-force attack in half and makes credential stuffing, where attackers try passwords leaked from other sites, far more effective.

WordPress core developers don’t consider usernames secret, and there’s a reasonable argument for that view: security should rest on strong passwords and two-factor authentication, not on hiding usernames. I agree with that in principle. But in practice, there’s no benefit in handing attackers half the puzzle, and closing these leaks takes very little effort.

Where usernames leak

There are several routes. Closing only one or two leaves the others open, so it’s worth knowing all of them.

The REST API users endpoint

The /wp-json/wp/v2/users endpoint lists users who have published posts in public post types. It’s used by the block editor, for example to show author selection, but the editor uses it while logged in. Anonymous visitors rarely have a legitimate reason to query it.

Author archives and the author query parameter

Every user with published posts has an author archive at /author/user-slug/. Visiting /?author=1, /?author=2 and so on redirects to those archives, revealing the slug for each user ID in turn. Attack tools loop through IDs to collect every author.

Author sitemaps

Since WordPress 5.5, core generates XML sitemaps, including /wp-sitemap-users-1.xml, which lists author archive URLs. SEO plugins generate their own author sitemaps too. Each URL includes the user slug.

oEmbed responses

When WordPress provides oEmbed data for a post, the response includes the author’s name and author URL, which contains the slug.

Post markup and feeds

Many themes print author links on every post, and RSS feeds include the author name. Display names are usually less of a problem, but author links reveal slugs.

Login error messages

Older versions of WordPress showed different errors for “unknown username” and “incorrect password”, which let attackers test whether a username existed. Modern WordPress uses more generic messages, but some plugins and custom login forms still reveal the difference. The password reset form can also confirm whether a username or email exists.

The root problem: slug equals username

By default, WordPress creates a user’s nicename, which becomes their URL slug, from their username. If your username is “jsmith”, your author archive is /author/jsmith/ and the REST API shows “jsmith” as the slug.

The cleanest long-term fix is to separate the two. If the public slug isn’t the login username, leaking it doesn’t matter much.

You can change the nicename with WP-CLI:

After that, the author archive becomes /author/editorial-team/, and the username stays private. Do this for every user who publishes content. Choose slugs that look intentional, such as a first and last name, rather than random strings that look odd in URLs.

Also make sure display names are set to something other than the username. In each user’s profile, choose a “Display name publicly as” option that uses their real name or a nickname.

Closing the REST API endpoint

Rather than disabling the REST API entirely, which breaks the block editor and many plugins, restrict the users endpoint to logged-in users.

Place this in a small must-use plugin in wp-content/mu-plugins so it can’t be accidentally deactivated. Logged-in users keep full access, so the editor works normally, while anonymous visitors get a 404 from that endpoint.

Check after enabling it. Some headless setups or plugins query the users endpoint anonymously to show author information. If something breaks, either allow those specific requests or expose a more limited custom endpoint with only the fields needed.

Blocking the author query enumeration

To stop /?author=N from revealing slugs, redirect or block those requests for anonymous visitors:

if ( is_admin()is_user_logged_in() ) {

You can also block this at the server, which is more efficient. In Nginx:

This doesn’t stop the author archive pages themselves. It only stops the numeric lookup that maps IDs to slugs.

Should you disable author archives?

That depends on your site. On a multi-author publication, author archives are useful for readers and can help with credibility and search visibility. On a small business site where every post is written by “admin”, they add nothing.

If author archives aren’t useful, disable them. Most SEO plugins have a setting to turn them off, redirecting them to the homepage and removing them from sitemaps. If you’d rather do it in code:

If you keep author archives, make sure slugs aren’t usernames, as described above.

Removing users from sitemaps

To remove the core users sitemap:

If you use an SEO plugin’s sitemaps instead, disable the author sitemap in its settings. Pirajki SEO, Yoast and Rank Math all have an option for this.

Tidying oEmbed and feeds

oEmbed author data is a minor leak, and if you’ve changed nicenames it doesn’t matter much. If you want to remove it, filter oembed_response_data and remove the author_url and author_name fields.

For feeds, the author name displayed is the display name, not the username, so once display names are set properly there’s little to worry about.

Check your login and password reset responses

Test your login form with a username that doesn’t exist and a username that does but with the wrong password. If the messages differ, you’re leaking information. Core WordPress now shows a fairly generic message, but custom login plugins and themes sometimes reintroduce specific ones.

The lost password form is trickier. Telling users “no account found with that email” is helpful for genuine users but confirms which addresses are registered. For most business sites, a neutral message like “If an account exists for that address, you’ll receive an email” is the better choice.

Testing that it worked

After making changes, log out or use a private browser window and check:

  • /wp-json/wp/v2/users should return a 404 or an error, not a list.
  • /?author=1 should not redirect to an author slug.
  • /wp-sitemap.xml should not list a users sitemap.
  • Author links in your theme should show the new slugs, not usernames.

Then run a WordPress scanner like WPScan against your site with its user enumeration option. If it finds no usernames, or only slugs that aren’t login names, you’re done.

Keep perspective

Hiding usernames is a small improvement, not a security strategy. An attacker who doesn’t know your username can still find it through other means, such as an old email, a public profile or a guess like “admin”. The real protection comes from strong unique passwords, two-factor authentication on every account with publishing rights and login rate limiting.

Think of user enumeration fixes as tidying up. They reduce noise, make automated attacks less efficient and remove an easy win for attackers. They take half an hour to implement and almost never cause problems. That’s a good trade, as long as you don’t mistake it for the whole job.

Multisite and WooCommerce considerations

On a multisite network, the users endpoint and author archives exist on every subsite, so apply the must-use plugin at the network level rather than per site. Super admin usernames are the most valuable target on a network, so give those accounts public slugs that differ from their logins, or better still, have them publish nothing at all and use separate author accounts for content.

WooCommerce stores have an extra wrinkle. Customers have accounts too, and some themes or review plugins link reviewer names to profile pages. Check product reviews while logged out and make sure they show a first name or display name only, with no link that reveals a customer’s slug. Customer accounts are rarely targeted directly, but leaking them is still a privacy problem you don’t need. Closing WordPress user enumeration is a small, sensible step that pairs well with those stronger defences.

Frequently asked questions

Will blocking the users endpoint break the block editor?

No, as long as you only block it for logged-out visitors. The editor runs while you’re logged in, so it keeps full access.

Should I rename the “admin” username?

Yes, if you still have one. WordPress doesn’t let you change usernames directly, so create a new administrator with a different username, log in as them and delete the old account, attributing its content to the new one.

Does changing the nicename affect SEO?

Author archive URLs change. If those pages are indexed and matter to you, add redirects from the old author URLs to the new ones. For sites without valuable author pages, it makes no difference.

Do security plugins handle this?

Many do, with a single “prevent user enumeration” option. Check which routes they cover. Some only handle the author query parameter and leave the REST API endpoint open.