I have upgraded my WordPress to 4.7.1, and after that I've tried to enumerate users through REST API, which should be fixed, but I was able to retrieve users.



[{"id":1,"name":"admin","url":"","description":"","link":"https:\/\/mywebsite\/author\/admin\/","slug":"admin","avatar_urls":{"24": ...

Changelog from latest version:

The REST API exposed user data for all users who had authored a post of a public post type. WordPress 4.7.1 limits this to only post types which have specified that they should be shown within the REST API. Reported by Krogsgard and Chris Jean.

After installing plugin Disable REST API, it seems that everything is working fine, but I don't like to use for every little thing plugin.

The output after using plugin is:

{"code":"rest_cannot_access","message":"Only authenticated users can access the REST API.","data":{"status":401}}

How can I fix this issue without using plugin, or why even after upgrading this stil exist?

EDIT 30.9.2017

I realized that there is a conflict between contact 7 plugin and Disable REST API and that will give you 401 unauthorized error.

When you try to send a message through contact 7 form, it will make a request


and disabling that is not a good idea.

  • 8
    As of my understanding, the changelog doesn't say that users don't get exposed anymore. i think it should be read as "Exposure gets limited to users who have authored post types which are set to be exposed via REST API." So as soon as a users authors a post for a post type which gets exposed (in contrast to just being public), the author will also be exposed. – JHoffmann Jan 13 '17 at 8:04
  • Maybe this link could be help you: wordpress.stackexchange.com/questions/228585/… – Pablo Jul 25 '17 at 2:23

Use this code snippet it will hide the users list and give 404 as the result, while rest of the api calls keep running as they were.

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;

You can refer to the this link on gitHub repo of WP_REST_API for some more details on same.


To remove all default REST API end-points you have to add following code:

<?php remove_action('rest_api_init', 'create_initial_rest_routes', 99); ?>

| improve this answer | |
  • as per the refered link you can also filter out the endpoints... – BlueSuiter Jan 27 '17 at 18:46
  • 1
    This is the best solution so far. – mirsad Sep 30 '17 at 2:02
  • Where does this custom code go? You do not mention where this is to be saved. – wruckie Dec 5 '17 at 20:31
  • You can keep this in functions.php of your theme. – BlueSuiter Dec 6 '17 at 6:15
  • This solution disables all CRUD operations on users, see this implementation only for GET requests: github.com/szepeviktor/wordpress-fail2ban/commit/… – Szépe Viktor Feb 28 '19 at 9:25

Remove the API link from the HTML head if you like.

// https://wordpress.stackexchange.com/a/211469/77054
// https://wordpress.stackexchange.com/a/212472
remove_action( 'wp_head', 'rest_output_link_wp_head', 10 );

Then require authentication for all requests.

// You can require authentication for all REST API requests by adding an is_user_logged_in check to the rest_authentication_errors filter.
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;

This will leave you with the desired message.

Now to stop enumeration you could use something like this.

// https://perishablepress.com/stop-user-enumeration-wordpress/
// block WP enum scans
    // https://m0n.co/enum
    if (!is_admin()) {
        // default URL format
        if (preg_match('/author=([0-9]*)/i', $_SERVER['QUERY_STRING'])) die();
        add_filter('redirect_canonical', 'shapeSpace_check_enum', 10, 2);
    function shapeSpace_check_enum($redirect, $request) {
        // permalink URL format
        if (preg_match('/\?author=([0-9]*)(\/*)/i', $request)) die();
        else return $redirect;

Check out the whole post for further techniques.

| improve this answer | |

You can fix it through nginx/apache config:

location ~* /wp-json/wp/v2/users {
        allow ip_address;
        deny all;
| improve this answer | |

just another answer:

add_filter( 'rest_user_query', '__return_null' );
add_filter( 'rest_prepare_user', '__return_null' );
| improve this answer | |

To be able to fix this you first need to know the source of the problem.

  1. Do you use SEO plugins like: All in one SEO pack or Yoast? Try to disable this and check again.
  2. Do you use the Jetpack plugin? Try to disable this and check again.

Please let me know if this pointed you into the right direction.

A dirty way to solve this is to just block the url underneath in your .htacces. https://mywebsite.com/wp-json/wp/v2/users

| improve this answer | |
  • The first two points you make are nonsense, but the "dirty" method of blocking access is actually faster than using WordPress since it will be blocked at Apache level before WordPress is even loaded. – Alexander Holsgrove Feb 18 at 10:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.