Get Started
Guides May 4, 2026 6 min read

WordPress REST API Security: Risks and How to Lock It Down

The WordPress REST API is one of the most powerful features in modern WordPress. It powers the block editor, mobile apps, and countless plugin integrations. But it also exposes your site’s data to anyone who sends a request. By default, the REST API reveals user information, post content, and site structure, all without requiring authentication. Many site owners have no idea that their user list is publicly accessible through a simple API call.

Here is what the REST API exposes, why it matters, and how to lock it down without breaking your site’s functionality.

What the REST API Leaks by Default

Run this command against almost any WordPress site:

curl https://yoursite.com/wp-json/wp/v2/users/

If the site has user enumeration exposed (which most do by default), you get back a JSON list of all users with their IDs, names, and avatars. This is a goldmine for attackers. They now have valid usernames for user enumeration attacks against your site. With valid usernames, brute force attacks become significantly more effective because the attacker no longer needs to guess both the username and password. They only need to guess the password.

The REST API also exposes post slugs, dates, category structures, and custom post types. None of this is sensitive on its own, but combined, it gives attackers a complete map of your site. They can see what plugins you use from the post types, what themes from the custom endpoints, and how your content is structured. This reconnaissance phase is often the first step in a targeted attack.

Disable REST API User Endpoint

The most important fix is to block the users endpoint. This stops attackers from scraping your username list. Add this code to your theme’s functions.php or a custom plugin:

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;
});

This removes the user listing endpoint entirely. Authenticated administrator requests still work through other internal mechanisms, but anonymous visitors get a 404 when they try to enumerate users via REST. Test this after installation by running the curl command again from an incognito browser window.

Require Authentication for All REST Requests

If your site does not use the REST API for unauthenticated visitors (no frontend JavaScript that fetches data from the API), you can require authentication for every REST request. This is the most restrictive option and provides the best protection.

add_filter('rest_authentication_errors', function($result) {
    if (!is_user_logged_in()) {
        return new WP_Error(
            'rest_not_logged_in',
            'You must be logged in to access the REST API.',
            array('status' => 401)
        );
    }
    return $result;
});

Be careful with this approach. Many plugins and the block editor rely on the REST API even for logged-out users. Test thoroughly after enabling it. If your site uses a frontend framework like React or Vue that fetches posts via REST for anonymous visitors, this will break it. In that case, consider a whitelist approach instead of a blanket block.

Block REST API Access Completely

For simple sites with no JavaScript frontend, you can disable the REST API entirely for logged-out users. This removes a significant attack surface with almost no impact on site functionality.

add_filter('json_enabled', '__return_false');
add_filter('json_jsonp_enabled', '__return_false');

remove_action('wp_head', 'rest_output_link_wp_head', 10);
remove_action('wp_head', 'wp_oembed_add_discovery_links', 10);
remove_action('template_redirect', 'rest_output_link_header', 11);

This removes REST API links from your site’s headers and disables the JSON endpoints. The block editor still works because it uses a separate authenticated endpoint with a nonce, but anonymous REST access is gone. You can verify this by visiting /wp-json/wp/v2/posts from an incognito browser.

Rate Limit REST API Calls

Even with restricted endpoints, attackers can still probe your site. Rate limiting REST API calls prevents automated scraping and enumeration. A single bot can make thousands of API requests per minute, trying different patterns and endpoint combinations.

Add this to your server’s Apache configuration:

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_URI} ^/wp-json/
  RewriteCond %{REMOTE_ADDR} ^(.*)$
  RewriteRule .* - [E=API_IP:%1]
</IfModule>

Pair this with fail2ban on your server. Monitor Apache access logs for /wp-json/ requests and set thresholds that trigger temporary IP bans. This catches aggressive scrapers and enumeration bots before they cause damage. A reasonable threshold is 100 API requests from a single IP within 60 seconds.

Remove REST API Links from Page Headers

Even if you disable endpoints, WordPress still outputs REST API URLs in your page headers. These header links tell automated scanners exactly where to find your API. Remove them to reduce your site’s attack surface:

remove_action('wp_head', 'rest_output_link_wp_head', 10);
remove_action('wp_head', 'wp_oembed_add_discovery_links', 10);

After removing these, check your site’s HTML source for any remaining REST API references. View the page source in your browser and search for “wp-json”. If none appear, the removal was successful. This is a small change, but it reduces the information available to automated scanners.

Monitor for REST API Abuse

Check your server logs regularly for unusual REST API activity. A sudden spike in /wp-json/ requests from a single IP is a strong indicator of an ongoing enumeration attack. Use fail2ban or a similar tool to automatically block such IPs.

grep "wp-json" /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -10

This command shows the top 10 IPs hitting your REST API. If one IP accounts for hundreds of requests per minute, block it at the firewall level. Run this check weekly as part of your routine security monitoring. Combine these steps with .htaccess protection for your wp-admin directory for layered security.

The Bottom Line

The WordPress REST API is useful, but its default configuration leaks more data than most site owners realize. Block the user enumeration endpoint, require authentication where possible, and monitor for abuse. These steps take minutes to implement and significantly reduce your attack surface. Every WordPress site should have at least the user endpoint disabled. For most sites, pairing that with REST API rate limiting and header removal provides strong protection against enumeration and reconnaissance attacks.

CORS Configuration for the REST API

If your site accepts REST API requests from external domains, configure CORS (Cross-Origin Resource Sharing) headers properly. A misconfigured CORS policy lets any website read your API responses, bypassing browser security controls. This matters if you have a mobile app or a separate frontend that fetches data from your WordPress REST API.

Add this to your server config to restrict origins:

Header always set Access-Control-Allow-Origin "https://your-frontend.com"
Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header always set Access-Control-Allow-Headers "Content-Type, Authorization"

Only allow origins you explicitly trust. Never use a wildcard (*) for the Access-Control-Allow-Origin header on the REST API. If an attacker tricks a logged-in admin into visiting a malicious site, that site can make authenticated REST API requests on their behalf if CORS is too permissive. This is a common attack vector called cross-site request forgery.

Turn Off oEmbed Discovery

WordPress’s oEmbed feature allows other sites to embed your content. It also adds REST API discovery links to your page headers. If you do not need oEmbed on your site, disable it to reduce the number of API endpoints exposed to anonymous visitors:

remove_action('wp_head', 'wp_oembed_add_discovery_links');
remove_action('wp_head', 'wp_oembed_add_host_js');
add_filter('embed_oembed_discover', '__return_false');

Most WordPress sites receive very little traffic from embedded content. Disabling oEmbed has no visible impact on your site’s functionality for normal visitors and removes another API surface that scanners can probe. If you run a large media site, test this change with your content sharing workflow before applying it permanently.

Related Articles