Ports
WordPress REST API user enumeration: what it leaks, and the exact fix.
Published 2026-06-08 · Last updated 2026-06-08 · Vantyris editorial
Visit `/wp-json/wp/v2/users` on most WordPress sites and you'll get back a JSON array with the public slug of every account that has authored a post. Those slugs are usually the login names. Attackers feed them into credential-stuffing and password-spray tools that hammer `/wp-login.php`. The endpoint is intended to be public — WordPress uses it to render author archives — but for a small-business site that rarely needs that, locking it down is a one-filter PHP edit.
What this means for your business
- The endpoint returns the slug, display name, and ID of every published-content author. The slug is usually identical or near-identical to the username. Combined with `/wp-login.php` being publicly reachable, this gives an automated attacker a clean target list — they no longer have to guess usernames before guessing passwords.
- It does NOT expose hashed passwords, email addresses, or accounts that haven't authored content. Subscriber-only or admin-only accounts that haven't published a post don't appear. The risk is reconnaissance, not direct compromise.
- Most modern WordPress brute-force botnets check `/wp-json/wp/v2/users` first because it's faster and quieter than the classic `?author=1` enumeration loop. Closing the endpoint moves your site off the easy-target list without breaking the front-end.
How to fix
Add a `rest_endpoints` filter that unsets the `/wp/v2/users` endpoint for unauthenticated requests. Five-line snippet in your theme's `functions.php` or a small must-use plugin.
- Decide where to put the code. If your site has a child theme, the child theme's `functions.php` is the right place — it survives core theme updates. Otherwise create a tiny must-use plugin: `wp-content/mu-plugins/disable-user-rest.php` with a `<?php` opener and the snippet. Must-use plugins survive theme changes too.
- Drop in the filter. Paste the following. The `unset` on the regex version handles requests like `/wp/v2/users/1`; without it, attackers can still walk users by ID. ``` 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; }); ```
- Verify the fix. Open `https://your-site/wp-json/wp/v2/users` in an incognito browser. You should get a 404 or 401 response, not a JSON array. Also try `https://your-site/?author=1` — if your site still 301-redirects to `/author/<slug>/`, you also need to block the classic enumeration path; see the gotcha below.
- Re-scan to confirm. Run a Vantyris scan after the change. The `wp.rest_users_endpoint_protected` healthy signal should appear. If the finding still shows as `wp.rest_user_enum`, something else exposed users — check Wordfence or a cache layer that might serve a stale 200 from before the change.
Owner: Your web developer or whoever maintains the WordPress theme. · Time: About 30 minutes including verification.
Common gotchas
- If you actually use author archives on the front-end (most blogs do), this endpoint also powers them through the admin-side. The fix above only unsets the public REST route, not the admin-side query, so author archives keep working. Test the archives after the change.
- Also block `?author=1` enumeration. WordPress's default behaviour is to 301-redirect `/?author=N` to `/author/<slug>/`, which leaks the same data the REST endpoint did. Wordfence and iThemes Security have one-click toggles for this; manual is a rewrite rule that returns 404 for any URL with `author=` in the query string.
- If you run a custom plugin that talks to `/wp/v2/users` over an authenticated REST connection, the filter above breaks it. Use `rest_authentication_errors` instead to allow the endpoint only for logged-in users.
- Caching layers (Cloudflare, WP Rocket, LiteSpeed Cache) may serve a stale 200 for the old endpoint after the change. Purge the cache to confirm the lockdown took effect.
How to verify the fix
From a browser in incognito mode (no cookies, no logged-in state), open `/wp-json/wp/v2/users`. The healthy result is 401, 403, or 404 — anything other than a JSON array of user objects. Also confirm `/wp-json/wp/v2/users/1`, `/wp-json/wp/v2/users/2`, and `/?author=1` all return non-200 responses. Re-running a Vantyris scan documents the closure with an audit-trail row.
Cyber Essentials alignment
This finding informs the following UK NCSC Cyber Essentials control areas:
- A4. User access control — accounts assigned the minimum required privileges.
Vantyris is not a CE certifying body. The mapping above is informational.
Common follow-up questions
Does this break anything on my front-end?
Almost never. Author archives render from server-side queries, not from the REST API. Subscription forms, contact forms, and most page-builder plugins don't touch this endpoint either. If you have a bespoke React or single-page front-end that authenticates to WordPress and reads users, that integration breaks — switch it to using `rest_authentication_errors` to allow authenticated reads instead.
Should I just install Wordfence and skip the code edit?
Wordfence (free tier) has a one-click toggle that does essentially the same thing. If you're already running it, that's the faster path. The code-edit version above is for sites that don't want another full plugin just for this one fix.
Will this stop brute-force attacks against /wp-login.php?
It removes the username-list source. It does not stop the brute-force itself — for that you need rate-limiting (Wordfence, iThemes), a WAF rule on /wp-login.php, and account-level 2FA. The endpoint lock-down is one layer of defence; the others sit on the login page itself.
Why isn't this disabled by default in WordPress?
The endpoint is part of the REST API contract — WordPress's headless / Gutenberg / Jetpack functionality assumes it's there. Disabling it by default would break too much. The core team's position is that exposing public author slugs isn't a vulnerability; treating it as recon is the consumer's call.
References
- WordPress REST API: Users endpoint Vendor
- OWASP: Username enumeration OWASP
- NCSC: Password administration for system owners NCSC
Related explainers
- HSTS: the security header that locks HTTPS on for good.
- What a passive security scan can and cannot prove about your site.
Want Vantyris to scan your domain for this and 80 other findings?
Free teaser scan, no card. Verified scan from €10 with the full workspace around it: workflow, score trend, three PDF layouts, share links, monitoring.
Vantyris editorial team · methodology v1.0.0