How Attackers Find Your WordPress Admin Username — and How to Stop Them
· 10 min read
Last month I was reviewing access logs on a client's WooCommerce store and spotted a pattern: hundreds of requests cycling through ?author=1, ?author=2, ?author=3 — followed an hour later by a sustained brute force attack against wp-login.php targeting the author slugs those requests had revealed.
The attacker didn't guess the login names. WordPress pointed them in the right direction.
This technique is called user enumeration, and it's the reconnaissance step that precedes almost every brute force attack. According to Patchstack's 2026 data, enumeration remains the entry point for roughly 40% of brute force incidents against WordPress sites. The good news is that every enumeration vector can be shut down in minutes.
A Quick Note on Slugs vs. Login Names
Technically, what WordPress exposes publicly is the author slug (user_nicename), not the actual login name (user_login). The REST API only returns user_login in the authenticated edit context. However, when WordPress creates a new user account, it sets user_nicename to the same value as user_login by default. Unless someone has deliberately changed the slug afterwards, the publicly visible author slug is the login name. In practice, on most sites I audit, they match — which is exactly why attackers bother with enumeration in the first place.
The Four Ways Your Author Slug Is Exposed
WordPress exposes author slugs through four separate mechanisms. Most guides only cover one or two. If you leave any of them open, attackers will find it.
1. Author Archives (?author=1)
This is the oldest trick. Visit yoursite.com/?author=1 and WordPress redirects to yoursite.com/author/admin/ — revealing that user ID 1's author slug is admin. Attackers script this from 1 to 50 and harvest every author slug on the site in seconds.
Test it now: Open a private browser window and visit your site with ?author=1 appended. If you see a redirect to /author/your-slug/, you're exposed.
2. REST API User Endpoint (/wp-json/wp/v2/users)
Since WordPress 4.7, the REST API ships with a public users endpoint. No authentication required. Visit yoursite.com/wp-json/wp/v2/users and you get a JSON response listing every user who has published a post, complete with their author slug and display name.
Test it now:
curl -s https://yoursite.com/wp-json/wp/v2/users | python3 -m json.tool
If you see a list of user objects with "slug" fields containing author slugs (which typically match the login name), you're exposed.
3. oEmbed Responses
When another site embeds one of your posts, WordPress serves an oEmbed response that includes the author's name and a link to their author archive. An attacker doesn't need another site to exploit this — they just request the oEmbed data directly:
curl -s "https://yoursite.com/wp-json/oembed/1.0/embed?url=https://yoursite.com/"
The response includes "author_name" (display name) and "author_url" (which contains the author slug in the URL) fields that reveal who published the page.
4. User Sitemaps
WordPress 5.5 introduced auto-generated sitemaps, including a users sitemap at yoursite.com/wp-sitemap-users-1.xml. This XML file lists every author on the site with links to their author archive pages — author slugs included.
Test it now: Visit yoursite.com/wp-sitemap-users-1.xml in your browser. If you see an XML document listing authors, that's another enumeration vector.
Fixing All Four Vectors
You have two options: handle it at the WordPress level with PHP code, or block it at the server level with nginx rules. I recommend doing both — defence in depth.
Option A: PHP Fixes (functions.php or a Custom Plugin)
Add the following to your theme's functions.php or, better, to a site-specific plugin so it survives theme updates.
Block author archive enumeration:
add_action('template_redirect', function () {
if (is_author()) {
wp_redirect(home_url('/'), 301);
exit;
}
});
This redirects all author archive requests to the homepage. If you use author archives for a multi-author blog, you can instead block only the numeric query string variant:
add_action('template_redirect', function () {
if (isset($_GET['author']) && is_numeric($_GET['author'])) {
wp_redirect(home_url('/'), 301);
exit;
}
});
Remove the REST API users endpoint:
add_filter('rest_endpoints', function ($endpoints) {
if (isset($endpoints['/wp/v2/users'])) {
unset($endpoints['/wp/v2/users']);
}
if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
return $endpoints;
});
This removes the users endpoint entirely for everyone, including authenticated users. If you need the endpoint for admin use (some plugins rely on it for user search in the editor), use a conditional check instead:
add_filter('rest_endpoints', function ($endpoints) {
if (!current_user_can('list_users')) {
unset($endpoints['/wp/v2/users']);
unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
return $endpoints;
});
Strip author data from oEmbed responses:
add_filter('oembed_response_data', function ($data) {
unset($data['author_name']);
unset($data['author_url']);
return $data;
});
Disable the users sitemap:
add_filter('wp_sitemaps_add_provider', function ($provider, $name) {
if ($name === 'users') {
return false;
}
return $provider;
}, 10, 2);
Option B: Nginx Rules (Server-Level Blocking)
If you manage your own server or VPS, add these rules inside your site's server block in nginx. Server-level blocking is faster and stops requests before PHP even loads.
# Block author enumeration via query string
if ($query_string ~ "author=([0-9]+)") {
return 403;
}
# Block REST API user endpoint
location ~* /wp-json/wp/v2/users {
return 403;
}
# Block user sitemap
location ~* /wp-sitemap-users-[0-9]+\.xml {
return 403;
}
After adding the rules, test and reload nginx:
sudo nginx -t && sudo systemctl reload nginx
What About Security Plugins?
Plugins like Wordfence, Solid Security (formerly iThemes Security), and the dedicated Stop User Enumeration plugin can handle this too. If you already run one of these, check its settings — most have an enumeration protection toggle that's off by default. But if you're comfortable adding the code above, you don't need another plugin for this. Every plugin adds overhead and attack surface.
Why This Matters Even If You Use Strong Passwords
I sometimes hear "my password is strong, so it doesn't matter if they know my username." That logic has two problems.
First, username plus password is still the primary authentication pair. Knowing the username cuts the attacker's work in half. Automated tools like WPScan and Hydra accept username lists and will hammer wp-login.php with credential-stuffing dictionaries for days.
Second, if an attacker sees that your admin username is something like mike or admin, they'll assume the site isn't well-maintained and escalate their efforts. A site that returns 403 on every enumeration vector signals that someone is paying attention — and most automated scanners move on to easier targets.
Verify Your Fixes
After applying the changes, test all four vectors from outside your network. Use a phone on mobile data or a VPN to avoid cached results:
# Author archive
curl -sI "https://yoursite.com/?author=1" | grep -i location
# REST API
curl -s "https://yoursite.com/wp-json/wp/v2/users"
# oEmbed
curl -s "https://yoursite.com/wp-json/oembed/1.0/embed?url=https://yoursite.com/"
# Users sitemap
curl -sI "https://yoursite.com/wp-sitemap-users-1.xml" | grep HTTP
You should see 403 responses or redirects to the homepage — not author slugs.
If you've already been brute-forced and want to see how I handle that at the server level, I've written a detailed guide on blocking login attacks with Fail2Ban and nginx. Enumeration prevention and login protection work together — shut down reconnaissance first, then harden the door.
Stop Firefighting. Start Maintaining.
I manage 70+ WordPress sites for UK agencies and businesses. Whether you need ongoing maintenance, emergency support, or a one-off security hardening — I can help.
