Since WordPress 4.7, the REST API has been enabled by default. It powers the Block Editor (Gutenberg), allows “Headless” WordPress setups, and lets plugins communicate with external services.
However, by default, the REST API is also publicly accessible.
Anyone—including hackers and bots—can query your site’s API endpoints (/wp-json/) to extract data about your users, posts, and taxonomy structure. While this isn’t always a “vulnerability” in the strict sense, it is a massive information leak that aids attackers in the reconnaissance phase.
In this guide, we’ll explore the specific risks of an open REST API and provide the code snippets you need to lock it down.
The Core Risk: Information Disclosure
The REST API is designed to be open. If you visit https://yoursite.com/wp-json/, you get a JSON response listing all available routes and endpoints.
While convenient for developers, this openness creates three major security problems:
1. User Enumeration (The #1 Threat)
By default, WordPress allows anyone to query the users endpoint:
GET /wp-json/wp/v2/users
The Risk: This request returns a list of all users who have published posts, including their User IDs, Display Names, and most critically, their Usernames (slugs).
Once a hacker has your username (e.g., admin_ted), they have 50% of the credentials needed to log in. They can then launch a targeted Brute Force attack on that specific account.
FunSentry’s Scan:
When you run a FunSentry scan, we specifically target this endpoint. If we receive a 200 OK status and a list of users, we flag your site as “Vulnerable to User Enumeration.”
2. Content Scraping
Scrapers can use /wp-json/wp/v2/posts to download your entire content library in a structured format, bypassing your theme’s frontend design. This makes it incredibly easy for content thieves to clone your site.
3. DDoS and Resource Exhaustion
API requests can be more resource-intensive than standard page loads because they often trigger complex database queries. Attackers can flood your API endpoints to exhaust your server’s CPU and memory, taking your site offline.
How to Secure the REST API (Without Breaking Your Site)
Important: You generally cannot completely disable the REST API. The WordPress Block Editor (Gutenberg), WooCommerce, and many modern plugins (like Contact Form 7) rely on it to function.
Instead of disabling it, we must harden it.
1. Disable User Enumeration Only
This is the most critical fix. We want to keep the API open for functionality but stop it from revealing user data.
Add this code snippet to your theme’s functions.php file or a custom plugin:
PHP
/**
* Disable REST API User Enumeration
* Blocks access to /wp-json/wp/v2/users for unauthenticated requests
*/
add_filter( 'rest_endpoints', function( $endpoints ) {
if ( isset( $endpoints['/wp/v2/users'] ) && ! is_user_logged_in() ) {
unset( $endpoints['/wp/v2/users'] );
}
if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) && ! is_user_logged_in() ) {
unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
}
return $endpoints;
});
What this does:
It removes the /users endpoint from the API registry unless the person requesting it is currently logged in as an administrator or editor. Bots scanning your site will now get a 404 Not Found.
2. Disable REST API for Non-Logged-In Users (Strict Mode)
If your site does not use a headless frontend and you want maximum security, you can block all anonymous REST API access.
Warning: This may break some public-facing plugins that use AJAX via the REST API. Test thoroughly!
PHP
/**
* Require Authentication for All REST API Requests
*/
add_filter( 'rest_authentication_errors', function( $result ) {
// If a valid authentication is already present, allow it.
if ( ! empty( $result ) ) {
return $result;
}
// If the user is not logged in, block the request.
if ( ! is_user_logged_in() ) {
return new WP_Error(
'rest_not_logged_in',
'You are not currently logged in.',
array( 'status' => 401 )
);
}
return $result;
});
What this does:
Any request to /wp-json/ from an unauthenticated user gets a 401 Unauthorized error. This effectively hides your API from the public internet while allowing the Block Editor (which is authenticated) to work for you.
3. Restrict JSONP Support
JSONP (JSON with Padding) is an older technique used to bypass cross-domain policies. It is rarely needed in modern WordPress setups but is enabled by default.
PHP
// Remove JSONP support
remove_filter( 'rest_jsonp_enabled', '_return_true' );
Using “Application Passwords” Safely
WordPress 5.6 introduced Application Passwords, allowing external systems to authenticate via the REST API without using your main password.
The Risk:
If a hacker tricks an administrator into generating an application password (social engineering), or if they gain access to a less-secure system you’ve connected to, they have full admin access to your site via the API.
Best Practice:
- Go to Users → Profile.
- Scroll down to Application Passwords.
- Audit this list regularly. Revoke any passwords for apps you no longer use.
- If you don’t use external integrations, disable this feature entirely using a plugin like “Disable Application Passwords.”
Summary Checklist
| Action | Impact on Functionality | Security Gain |
Disable /users endpoint | None (Safe) | ⭐⭐⭐⭐⭐ (Stops user harvesting) |
| Require Auth for all API | High (May break plugins) | ⭐⭐⭐⭐⭐ (Complete API lockdown) |
| Disable JSONP | Low | ⭐⭐ (Good hygiene) |
| Audit App Passwords | None | ⭐⭐⭐ (Prevents backdoor access) |
The REST API is a powerful tool, but it shouldn’t be an open book. At a minimum, every WordPress administrator should block the /users endpoint to prevent easy username harvesting.
Is your User Data Leaking?
Don’t wait for a brute force attack. Run a free scan at FunSentry to instantly check if your /wp-json/wp/v2/users endpoint is exposing your admin usernames to the world.
