Disclosure
The eight sites measured here are under this publication’s own management. That is the reason the data exists and it is also a conflict, so the method below is written to be re-run by anyone against their own site rather than taken on our word. No site is named. Nothing was written to any site during the measurement.
A WordPress install answers three anonymous questions about who writes on it, and on eight sites checked in one pass on 7 September 2026, every one answered at least one of them. Four gave up an account identifier to anybody who asked, with no login, no cookie and no rate limit reached.
The Three Routes
Each is a normal, documented part of WordPress. None is a vulnerability in the sense of a flaw for the vendor to patch. They are defaults.
The users route. GET /wp-json/wp/v2/users returns the site’s authors as JSON, including a slug for each. On four of the eight sites this returned 200 to an anonymous request.
The author route. GET /?author=1 redirects to /author/<name>/, and the name in that destination is the same identifier. Two sites redirected to a URL containing an account name.
The oEmbed route. GET /wp-json/oembed/1.0/embed?url=<home> returns an author_name field. Eight of eight answered this one, which makes it the route most likely to be overlooked, because the first two can be closed while this one keeps publishing.
What the Eight Sites Gave Up
- 4 sites — an account identifier via the users route or the author route, plus oEmbed.
- 2 sites — an account identifier via oEmbed alone, the other two routes closed.
- 2 sites — a brand display name only, no account identifier seen.
- 8 of 8 — answered the oEmbed route.
Three of the eight published the identifier admin. On one host in the sample, the login log showed a continuous automated stream against admin and root, with tens of thousands of lockouts since installation. The identifier being published and the identifier being attacked were the same string.
The Error We Made, and Who Caught It
The first version of this finding said these routes publish the login name. That is wrong, and it was wrong for a day.
The REST slug field is user_nicename, not user_login. They are different fields. WordPress derives the nicename from the login name at registration, so they are identical unless somebody deliberately changed one, which is why they almost always agree and why the mistake is easy to make and hard to notice.
It was caught by a second team working on a different site in the same estate, who did not take the claim on trust and checked it against the database with wp user get 1. On that site the login name turned out to be different from the nicename. The original claim was too strong. The correction was published into our own record within hours, with the original wording struck rather than deleted, and the finding was re-issued to everyone who had received the first version.
What that error would have cost, stated plainly: had nobody checked, the recommendation would have been to rename accounts on the strength of a field that was not the field we thought it was.
And the shape of the error is the same as the finding. A value was read in one place and asserted to be something else without confirming it. That is exactly what these three routes invite, because they return something that looks like a login name and the temptation is to treat it as one.
What survives the correction: a nicename is a strong indication of a login name and not proof of one. That is enough to justify closing the routes, because hiding a field that is usually correct costs nothing. It is not enough to justify renaming an account, which is disruptive. Confirm against the database before acting.
How to Check Your Own Site
Three requests, from a browser that is not logged in, replacing the domain:
https://example.com/wp-json/wp/v2/usershttps://example.com/?author=1https://example.com/wp-json/oembed/1.0/embed?url=https://example.com/
If the first returns a list rather than a 404, if the second lands on a URL containing a name, or if the third returns an author_name that looks like an account rather than a brand, the site is publishing an identifier.
The Fix, and Its Honest Value
The routes can be closed in a theme or a small plugin: deny the users REST route to unauthenticated requests, intercept the author query before WordPress redirects, and strip the author field from the oEmbed response. Ours is deployed on one site in the estate and written for the rest.
This is not a large security win and should not be sold as one. An account identifier is not a password, and a site with a strong password and login-attempt limiting is not compromised by publishing one. What closing the routes does is remove a free, reliable hint from an attacker who would otherwise have to guess, on the single most-guessed identifier in the world.
Two limits, and they bound everything above: the sample is eight sites under one operator, so it says nothing about how common this is in general, only that these three routes are open by default and stay open until somebody closes them; and the check was made once, on one day, so a site that passes today is passing rather than fixed.
Written as method and data rather than as a case study. There is no lab and no contradiction in it. If it is useful, the useful part is that you can run the same three requests against your own site in under a minute.