Back to blog
Tutorials5 min read

How to Secure Your WordPress REST API: A Complete Guide

March 15, 2026·WO Security Shield Team
rest apiwordpresssecurityhardeningapi
How to Secure Your WordPress REST API: A Complete Guide

The WordPress REST API is enabled by default on every WordPress installation. While it powers essential features like the block editor and many plugins, it also exposes endpoints that attackers actively exploit.

What the REST API exposes by default

Out of the box, anyone can access:

GET /wp-json/wp/v2/users     → Lists all users with IDs and usernames
GET /wp-json/wp/v2/posts     → All published posts with metadata
GET /wp-json/wp/v2/pages     → All published pages
GET /wp-json/wp/v2/media     → All media items with file paths
GET /wp-json/wp/v2/comments  → All approved comments with emails

The most dangerous is the users endpoint. It gives attackers a ready-made list of usernames for brute-force attacks — no guessing required.

Step 1: Disable user enumeration

The single most important REST API hardening step. Add this to your theme's functions.php or a custom plugin:

// Block unauthenticated access to user endpoints
add_filter('rest_endpoints', function ($endpoints) {
    if (!is_user_logged_in()) {
        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;
});

Step 2: Require authentication for sensitive endpoints

For sites that don't need public API access:

add_filter('rest_authentication_errors', function ($result) {
    if (true === $result || is_wp_error($result)) {
        return $result;
    }
    if (!is_user_logged_in()) {
        return new WP_Error(
            'rest_not_logged_in',
            'API access requires authentication.',
            array('status' => 401)
        );
    }
    return $result;
});

Warning: This will break the block editor for logged-out users and any frontend features that rely on the REST API. Only use this on sites where the API is purely for admin use.

Step 3: Rate-limit API requests

Brute-force attacks against the REST API bypass traditional login rate limiters because they target /wp-json/wp/v2/users/me with different credentials. A WordPress firewall (WAF) can help, but you should also implement rate limiting at the server level:

# Nginx rate limiting for WordPress REST API
limit_req_zone $binary_remote_addr zone=wpapi:10m rate=30r/m;

location /wp-json/ {
    limit_req zone=wpapi burst=10 nodelay;
    try_files $uri $uri/ /index.php?$args;
}

Step 4: Monitor API activity

WO Security Shield monitors REST API requests and flags:

  • Bulk user enumeration attempts
  • Unauthenticated requests to sensitive endpoints
  • Unusual request patterns (high volume from single IPs)
  • Failed authentication attempts through the API

Step 5: Disable XML-RPC alongside REST API hardening

Many site owners harden the REST API but forget that XML-RPC (xmlrpc.php) exposes similar functionality. If you're locking down the REST API, disable XML-RPC too:

add_filter('xmlrpc_enabled', '__return_false');

Or block it at the server level:

location = /xmlrpc.php {
    deny all;
    return 403;
}

Step 6: Remove REST API link headers

WordPress advertises its REST API in HTTP headers and HTML:

// Remove REST API link from HTML head
remove_action('wp_head', 'rest_output_link_wp_head');
// Remove REST API link from HTTP headers
remove_action('template_redirect', 'rest_output_link_header', 11, 0);

This doesn't disable the API, but it stops advertising its existence to automated scanners.


The REST API is a powerful tool, but it needs to be treated like any other attack surface — restricted by default and monitored continuously. Use WO Security Shield to detect API abuse before it leads to a compromise.

Understanding What the REST API Exposes

By default, the WordPress REST API exposes more information than most site owners realise:

Default Public Endpoints

Endpoint What it exposes Risk
/wp-json/wp/v2/users All user accounts with usernames Username enumeration for brute-force
/wp-json/wp/v2/posts All published posts with metadata Content scraping
/wp-json/wp/v2/pages All published pages Site structure mapping
/wp-json/wp/v2/media All media files with URLs Asset discovery
/wp-json/wp/v2/categories All categories and taxonomy Site structure
/wp-json/ Full API schema with all endpoints Attack surface mapping

The most dangerous is /wp-json/wp/v2/users — it gives attackers a list of valid usernames to target in brute-force attacks.

Securing Specific Endpoints

Block user enumeration (highest priority):

// Require authentication for user endpoints
add_filter('rest_authentication_errors', function($result) {
    if (!empty($result)) {
        return $result;
    }
    if (!is_user_logged_in()) {
        $route = untrailingslashit($GLOBALS['wp']->query_vars['rest_route'] ?? '');
        // Block user enumeration
        if (preg_match('/^\/wp\/v2\/users/', $route)) {
            return new WP_Error('rest_forbidden', 'Authentication required.', ['status' => 401]);
        }
    }
    return $result;
});

Restrict API to authenticated users only (for private/internal sites):

// Block all unauthenticated REST API access
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',
            'API access restricted to authenticated users.',
            ['status' => 401]
        );
    }
    return $result;
});

Caution: This will break any public features that use the REST API, including the Gutenberg editor for logged-out previews, some contact form plugins, and headless WordPress setups.

Rate Limiting the API

Without rate limiting, attackers can hammer your API endpoints thousands of times per minute:

// Simple rate limiter for REST API
add_filter('rest_pre_dispatch', function($result, $server, $request) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $transient_key = 'rest_rate_' . md5($ip);
    $requests = (int) get_transient($transient_key);

    if ($requests > 100) { // Max 100 requests per minute
        return new WP_Error(
            'rate_limit_exceeded',
            'Too many requests. Please slow down.',
            ['status' => 429]
        );
    }

    set_transient($transient_key, $requests + 1, MINUTE_IN_SECONDS);
    return $result;
}, 10, 3);

API Security Best Practices

  1. Use application passwords instead of cookie auth for external integrations (WordPress 5.6+)
  2. Never expose API keys in JavaScript — use a server-side proxy for third-party API calls
  3. Validate and sanitise all input — even on custom endpoints
  4. Use nonces for authenticated requestswp_create_nonce('wp_rest') in the frontend
  5. Monitor API access logs — unusual patterns indicate reconnaissance or attack

WO Security Shield

Is your WordPress site protected?

Run a free malware scan in under 2 minutes. No credit card required.