Skip to main content
Vantyris

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

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.

  1. 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.
  2. 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; }); ```
  3. 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.
  4. 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

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:

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

Related explainers

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.

Editorial

Vantyris editorial team · methodology v1.0.0