search ]

Secure the WordPress REST API (Without Breaking It)

The WordPress REST API is a powerful feature that enables developers to interact with a site’s data programmatically. While useful for headless setups, custom apps, and AJAX calls, it can also expose sensitive information if left unsecured.

By default, the REST API allows unauthenticated users to access a variety of endpoints, including details about users, posts, and taxonomies.

In this post, we will walk through several safe ways to secure your REST API without disrupting core functionality or plugin features that depend on it.

Why Secure the REST API?

Some REST API endpoints expose data that could be used by attackers for enumeration or reconnaissance. For example:

  • /wp-json/wp/v2/users – lists all registered users, including usernames and user IDs
  • /wp-json/wp/v2/posts – public by default, exposing post content and metadata
  • /?author=1 – redirects to the author archive, revealing the username in the URL

Securing these endpoints helps prevent brute-force attacks, credential stuffing, phishing, and content scraping.

Do not disable the REST API entirely. WordPress core features like the block editor (Gutenberg), many plugins, and the WordPress admin itself depend on it. Disabling it will break your site. Instead, restrict access to specific endpoints or require authentication where needed.

Option 1: Restrict Access to Authenticated Users

You can require authentication for all REST API requests by adding the following code to your theme’s functions.php or a custom plugin:

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',
            'You must be logged in to access the REST API.',
            array('status' => 401)
        );
    }

    return $result;
});

Note the check for true === $result || is_wp_error($result) at the top. This ensures that if another authentication handler has already validated or rejected the request, we do not override it.

This approach blocks all unauthenticated REST API access, including public AJAX calls from the frontend. Use it only if your site does not rely on public API endpoints.

Option 2: Restrict Specific Endpoints Only

If you want to restrict only certain endpoints (like the users endpoint) while keeping the rest of the API intact, use this more targeted approach:

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 both the users list endpoint and individual user endpoints. The block editor and other core features will continue to work normally since they do not depend on public access to user data.

Option 3: Block REST API for Non-Editors

You can limit REST API access based on user capabilities. For example, allow only editors and administrators:

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',
            'Authentication required.',
            array('status' => 401)
        );
    }

    if (!current_user_can('edit_posts')) {
        return new WP_Error(
            'rest_forbidden',
            'You do not have permission to access the REST API.',
            array('status' => 403)
        );
    }

    return $result;
});

This checks the edit_posts capability, which subscribers and contributors do not have. Using capabilities rather than role names is the correct approach – current_user_can('subscriber') checks a role name, not a capability, and may not work reliably.

Option 4: Hide Sensitive User Data

Instead of disabling the users endpoint entirely, you can filter out sensitive fields from the response:

add_filter('rest_prepare_user', function($response, $user, $request) {
    if (!current_user_can('list_users')) {
        $data = $response->get_data();
        unset($data['slug']);
        unset($data['link']);
        $response->set_data($data);
    }
    return $response;
}, 10, 3);

This removes the slug (username) and link (author archive URL) from the response for users who do not have the list_users capability. The endpoint still works, but the most sensitive data is hidden.

Option 5: Use Application Passwords for External Access

Since WordPress 5.6, Application Passwords provide a built-in way to authenticate REST API requests from external tools, scripts, or mobile apps.

Each Application Password is tied to a specific user account, individually revocable, and cannot be used to log into wp-admin. This makes them safer than sharing your main password with third-party services.

To create one, go to Users > Your Profile and scroll to the “Application Passwords” section. Give it a descriptive name (e.g., “Deploy Script” or “Mobile App”) and save the generated password immediately – it will not be shown again.

External requests authenticate using HTTP Basic Auth with your WordPress username and the Application Password.

Option 6: Use a Security Plugin

Plugins like Wordfence, iThemes Security, or Disable REST API give you toggles to limit access without writing code. These tools offer granular control over what is accessible and to whom.

For most sites, a combination of code-level restrictions (Options 1-4) and a security plugin provides the strongest protection.

Don’t Forget Author Enumeration

Restricting the REST API users endpoint is important, but it is not the only way attackers discover usernames. WordPress also exposes usernames through author archive URLs.

Visiting /?author=1 redirects to /author/admin/, revealing the username. To block this, add the following to your theme’s functions.php:

add_action('template_redirect', function() {
    if (is_author()) {
        wp_redirect(home_url('/'), 301);
        exit;
    }
});

This redirects all author archive requests to the homepage. If your site uses author archives for legitimate purposes, you can restrict this to non-logged-in users instead.

Testing Your API Security

After making changes, verify they work correctly. Test these URLs while logged out:

  • https://yoursite.com/wp-json/ – see available routes
  • https://yoursite.com/wp-json/wp/v2/users – check if user data is exposed
  • https://yoursite.com/?author=1 – check if author enumeration works

Use tools like Postman or your browser’s Developer Tools (Network tab) to inspect the response headers and body. Confirm that restricted endpoints return a 401 or 403 status code instead of user data.

Then log in and verify that the WordPress admin, block editor, and any plugins that use the REST API still function correctly.

FAQs

Common questions about securing the WordPress REST API:

Will disabling the REST API break my WordPress site?
Yes, if you disable it entirely. The block editor (Gutenberg), many plugins, and parts of the WordPress admin depend on the REST API. Instead of disabling it completely, restrict access to specific endpoints or require authentication for sensitive ones.
Is the /wp-json/wp/v2/users endpoint dangerous?
It exposes usernames and user IDs to anyone who visits the URL. While it does not reveal passwords or emails, knowing usernames gives attackers a starting point for brute-force and credential stuffing attacks. Restricting or removing this endpoint is one of the first things you should do.
What are Application Passwords in WordPress?
Application Passwords were introduced in WordPress 5.6 as a built-in way to authenticate REST API requests from external tools. Each password is tied to a user account, individually revocable, and cannot be used to log into wp-admin. They use HTTP Basic Auth and are the recommended method for scripts, mobile apps, and third-party integrations.
Should I use current_user_can with a role name or capability?
Always use capabilities, not role names. For example, use current_user_can('edit_posts') instead of current_user_can('editor'). Passing a role name to current_user_can() is unreliable and may not work as expected. If you need to check a specific role, use in_array('role_name', $user->roles) instead.
Do I need to secure the REST API if I use a security plugin?
Security plugins like Wordfence or iThemes Security can restrict REST API access, but they may not cover every endpoint or scenario. A combination of plugin-level protection and targeted code-level restrictions gives you the strongest security posture. At minimum, verify that the users endpoint is not publicly accessible.
Can I restrict the REST API using .htaccess?
Technically yes, but it is not recommended. Blocking /wp-json/ at the server level will break the block editor and any plugin that uses the REST API internally. The PHP-based approaches described in this guide are safer because they can distinguish between authenticated and unauthenticated requests and target specific endpoints.

Summary

The REST API is essential for modern WordPress, but securing it is equally important. Start by restricting the users endpoint (Option 2) since that is the most commonly exploited. Then evaluate whether you need broader restrictions based on your site’s needs.

For external integrations, use Application Passwords instead of sharing your main credentials. And do not forget to block author enumeration through /?author=N as well.

For a broader look at WordPress security, check out our guide on WordPress security hardening tips. You can also add security headers to further protect your site.

Join the Discussion
1 Comments  ]
  • Brian 10 May 2025, 3:48

    Thanks! it helped a lot!

Leave a Comment

To add code, use the buttons below. For instance, click the PHP button to insert PHP code within the shortcode. If you notice any typos, please let us know!

Savvy WordPress Development official logo