The WordPress REST API is a powerful feature that allows developers to interact with a site's data remotely. However, this accessibility comes with a significant security trade-off: by default, it often exposes sensitive user information. If you visit yourdomain.com/wp-json/wp/v2/users, you might be surprised to see a list of usernames, IDs, and Gravatar links for everyone who has published a post. This is known as user enumeration, and it is a common precursor to brute-force login attacks.
While WordPress has introduced some limitations in recent versions (specifically 4.7.1 and later), many sites still inadvertently leak user data. Simply disabling the REST API entirely with a plugin isn't always the best solution, as modern features like the Gutenberg editor and popular plugins like Contact Form 7 rely on these endpoints to function. In this guide, you will learn how to surgically secure your WordPress REST API to prevent user enumeration while keeping your site's functionality intact.
Why User Enumeration is a Security Risk
When an attacker wants to compromise a WordPress site, the first thing they need is a valid username. By accessing the /wp/v2/users endpoint, they can often retrieve a complete list of users who have authored public posts. With a list of usernames in hand, the attacker is already halfway to a successful brute-force attack.
Even if you have updated to the latest version of WordPress, the API may still expose users who are registered as authors. This is why a custom approach to security is often necessary to ensure your administrative accounts remain hidden from prying eyes.
Method 1: Removing Specific REST API Endpoints
The most effective way to stop user enumeration without breaking other API features is to target the specific endpoints that leak data. Instead of disabling the entire API, you can use the rest_endpoints filter to "unset" the users, posts, and comments routes.
Add the following code to your theme's functions.php file or a custom functionality plugin:
add_filter('rest_endpoints', function($endpoints){
$toRemove = ['users', 'posts', 'comments'];
foreach($toRemove as $val)
{
if (isset($endpoints['/wp/v2/'.$val])) {
unset($endpoints['/wp/v2/'.$val]);
}
if(isset($endpoints['/wp/v2/'.$val.'/(?P<id>[\\d]+)'])) {
unset($endpoints['/wp/v2/'.$val.'/(?P<id>[\\d]+)']);
}
}
return $endpoints;
});
This snippet specifically targets the user list and individual user data routes. When an unauthenticated user tries to access them, the server will return a 404 error, while other endpoints (like those used by Contact Form 7) remain fully operational.
Method 2: Requiring Authentication for All API Requests
If you want to go a step further and ensure that no data is accessible to the public via the REST API, you can require authentication for all requests. This is a great middle ground because it allows your site's internal processes and logged-in administrators to use the API while blocking everyone else.
add_filter( 'rest_authentication_errors', function( $result ) {
if ( ! empty( $result ) ) {
return $result;
}
if ( ! is_user_logged_in() ) {
return new WP_Error( 'rest_not_logged_in', 'Only authenticated users can access the REST API.', array( 'status' => 401 ) );
}
return $result;
});
When this filter is active, any anonymous request to your API will receive a 401 Unauthorized response. This effectively stops all enumeration bots in their tracks.
Method 3: Securing the API While Maintaining Gutenberg Compatibility
The Gutenberg block editor relies heavily on the REST API to fetch author data when you are writing posts. If you block the users endpoint too aggressively, you might find that you can no longer select authors in the post editor. To solve this, you can implement a role-based check.
add_filter( 'rest_endpoints', function( $endpoints ) {
if(is_user_logged_in()) {
$user = wp_get_current_user();
$roles = array('editor', 'administrator', 'author');
// If the user has a permitted role, allow access to the endpoints
if( array_intersect($roles, $user->roles ) ) return $endpoints;
}
// Otherwise, hide the user 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 approach ensures that your team can still manage the site effectively while the public-facing API remains secure.
Method 4: Server-Level Blocking via Nginx or .htaccess
For those who prefer to handle security at the server level, you can block access to the user endpoints before the request even reaches WordPress. This is highly efficient as it saves server resources.
For Nginx Users:
Add this to your configuration file:
location ~* /wp-json/wp/v2/users {
deny all;
}
For Apache Users (.htaccess):
Add this to your .htaccess file to block author scans:
# BEGIN block author scans
RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} (author=\\d+) [NC]
RewriteRule .* - [F]
# END block author scans
Frequently Asked Questions
Does disabling the REST API break Contact Form 7?
Yes, if you disable the entire API or use a plugin that blocks all /wp-json/ requests, Contact Form 7 will fail to submit forms, often resulting in a spinning icon or a 401 error. Use the targeted endpoint removal method (Method 1) to avoid this.
Why does WordPress expose users by default?
WordPress is designed to be open and social. The REST API allows external applications to display author profiles and post archives easily. While this is useful for some, most business websites prioritize security over this level of public data accessibility.
Can I still use the Block Editor (Gutenberg) if I hide users?
Yes, but you must ensure that logged-in users with administrative privileges are still allowed to access the API. Using the role-based filter in Method 3 is the best way to maintain Gutenberg compatibility while securing your site.
Wrapping Up
Securing the WordPress REST API is a critical step in hardening your website against modern threats. While the API provides immense value for developers, the default exposure of user data is a vulnerability you shouldn't ignore. By using the targeted code snippets provided above, you can stop user enumeration and protect your administrative accounts without sacrificing the functionality of your favorite plugins or the block editor.
Always remember to test these changes in a staging environment before applying them to your live site, especially if you rely on third-party integrations that might use the REST API.