The fix, in one snippet
add_filter('rest_authentication_errors', function ($r) {
return is_user_logged_in() ? $r : New WP_Error('rest_forbidden', '', ['status' => 401]);
});
Illustrative values. Change the paths, hostnames and options to match your own site before using it.
The sections below explain what is tested, why it matters and the alternatives.
What we test
Scan.now sends GET /wp-json/, falling back to /?rest_route=/ when pretty permalinks are off, and looks for a JSON document with a namespaces array and a routes object. When the index is readable without authentication the check records the number of namespaces and routes and which of them belong to plugins. It passes when the index answers 401 or 403 to an anonymous request, or when the routes object has been stripped. Individual endpoints are not enumerated beyond the index itself, and content routes such as /wp/v2/posts are not requested by this check; user enumeration through /wp/v2/users has its own finding under wordpress-user-enumeration. Rated informational: the index is public by design since the REST API endpoints arrived in WordPress 4.7, and Scan.now itself reads it as the first step of plugin discovery.
Why it matters
The index is not a vulnerability. It is the cheapest reconnaissance available. One request returns every namespace a plugin has registered (contact-form-7/v1, wc/v3, elementor/v1, yoast/v1), which maps almost one-to-one onto installed plugins, including ones that show nothing in the page. WPProbe's route-signature approach, which Scan.now follows, exists because this works so reliably; it also lists custom endpoints written for the site, which attackers read with particular interest since bespoke code has not had the scrutiny that popular plugins get. The trade-off is real: the REST API drives the block editor, the site health screen, mobile apps, Jetpack and many themes, so disabling it outright breaks things. Logged-in users can keep everything while anonymous visitors lose the map. Blocking the index does not stop a determined attacker who guesses routes or reads asset paths, so it belongs with the other friction measures in the WordPress scanning guide, after updates and plugin removal.
How to fix it
Refuse the index and namespace listings to anonymous requests while leaving /wp-json/wp/v2/ endpoints working, so the block editor (which runs logged in) and public content routes are unaffected. Add to a must-use plugin:
add_filter('rest_authentication_errors', function ($result) {
if (!empty($result) || is_user_logged_in()) {
return $result; // keep existing errors; logged-in users see everything
}
$route = untrailingslashit($GLOBALS['wp']->query_vars['rest_route'] ?? '');
if ($route === '' || preg_match('#^/[^/]+/v\d+$#', $route)) {
return new WP_Error('rest_index_disabled',
'The API index is not available to anonymous requests.', ['status' => 401]);
}
return $result; // /wp/v2/posts and other real endpoints still work
});
Also remove the discovery link and header that advertise the index location:
remove_action('wp_head', 'rest_output_link_wp_head');
remove_action('template_redirect', 'rest_output_link_header', 11);
Verify from outside:
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/wp-json/ # expect 401
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/wp-json/wp/v2/posts # expect 200
If a headless front end or an app relies on discovery through the index, leave it readable and put the effort into keeping the plugins it reveals updated.
Where this fits
REST API route index publicly readable is check 8 of 10 that the website vulnerability scanner runs under wordpress plugins, themes and core, ordered the way they are worth fixing. That ordering is the point: Fixing this one while the check above it still fails buys less than it looks like.
Fix this one first
Above it in the same category sits Installed themes identified (info), where the active theme (and its parent, if it is a child theme) was identified from asset paths in the page, with the version read from the theme's style.css header. An attacker who has that does not need this, so it is the better use of the same hour.
What fixing this still leaves open
REST API route index publicly readable closes one route in. Immediately below it: WordPress core version identified, where the exact WordPress core version could be read from the page or from readme.html; WordPress detected, where the site runs WordPress.
Found in the same scan
The website vulnerability scanner reports this alongside checks from other categories that are at least as serious, including Certificate chain and hostname validation, where the certificate presented for this hostname did not validate: The chain does not reach a trusted root, an intermediate is missing, the name does not match, or the certificate is self-signed or expired, and Exposed .env configuration file, where a .env configuration file is served from the web root. A single run of website vulnerability scanner answers all of them at once.
The first few lines
You are a senior web engineer. I ran a security and SEO scanner against my site and it reported the finding below. Fix it properly rather than suppressing the symptom.
Finding: REST API route index publicly readable (info severity)
Scanner check id: wordpress-rest-routes-exposed
The rest of this prompt names the pages and line numbers we found the problem on, the configuration to change, and the constraints a good answer has to respect. It is free, it just needs an account so the work is not scraped wholesale.
Sign in with GoogleSigning in is free and takes one click. We store your email address and nothing else.