Disable REST API without Plugins

Before I provide you a code snippet which you can copy and paste to your functions.php or something I would like to discuss why do even need to disable JSON REST API in WordPress?

The long story short is to provide less information about your website to those who shouldn’t probably have it. For example if we add at the end of site URL /wp-json/wp/v2/users, we can list all the registered users! Without emails of course but anyway.

If you want to prevent this from hapenning, you can either disable WordPress REST API completely or just its specific endpoints. And that’s what we’re going to do in this guide.

Completely Disable REST API

I believe that there are some cases when you need to completely turn the REST API off. And you can use the code below to achieve that:

add_filter( 'rest_authentication_errors', 'rudr_turn_off_rest_api' );

function rudr_turn_off_rest_api( $errors ) {

	// if there is already an error, just return it
	if( is_wp_error( $errors ) ) {
		return $errors;
	}

	// return WP_Error object
	return new WP_Error( 'no_rest_api_sorry', 'REST API not allowed', array( 'status' => 401 ) );

	return $errors;
	
}

If you don’t know where to insert the code, please read this. And yes, as you can see there is no need to do anything in .htaccess file.

Bad news – a complete REST API deactivation will block the Block Editor (Gutenberg) and maybe some third party plugins along the way. Like that:

REST API error not allowed in block editor
When you try to publish or update a post in Gutenberg with the disabled REST API, be ready for this error to appear.

So I would recommend you to consider conditional REST API deactivation at least for non-logged in users only and we’re going to talk about in the next chapter.

Disable REST API for Non-Logged in Users Only

Just by adding is_user_logged_in() condition in the code snippet above we can continue to use the Block Editor and any third party plugins while unauthorized users will not have the access to REST API.

Below is the modified version of the code:

add_filter( 'rest_authentication_errors', 'rudr_turn_off_rest_api_not_logged_in' );

function rudr_turn_off_rest_api_not_logged_in( $errors ) {

	// if there is already an error, just return it
	if( is_wp_error( $errors ) ) {
		return $errors;
	}
	
	if( ! is_user_logged_in() ) {
		// return WP_Error object if user is not logged in
		return new WP_Error( 'no_rest_api_sorry', 'REST API not allowed', array( 'status' => 401 ) );
	}
	
	return $errors;
	
}

It is also super-easy to modify this code in order to allow REST API for specific user roles only, all we need to do is to replace is_user_logged_in() function with current_user_can() one.

Just like this:

if( ! current_user_can( 'administrator' ) ) {
	// disable REST API for everyone except administrators
	return new WP_Error( 'no_rest_api_sorry', 'REST API not allowed', array( 'status' => 401 ) );
}

Restrict REST API by IP Addresses

In this another example we can just provide an array with IP addresses which are allowed to use REST API.

add_filter( 'rest_authentication_errors', 'rudr_allow_rest_api_for_ip' );

function rudr_allow_rest_api_for_ip( $errors ) {

	// if there is already an error, just return it
	if( is_wp_error( $errors ) ) {
		return $errors;
	}
	
	$whitelist = array( 
		'37.33.184.198',
		'62.74.24.234',
		// etc
	);
	
	if( ! in_array( $_SERVER[ 'REMOTE_ADDR' ], $whitelist ) ){
		// return WP_Error object if user is not logged in
		return new WP_Error( 'no_rest_api_sorry', 'REST API not allowed', array( 'status' => 401 ) );
	}
	
	return $errors;
	
}

Show 404 Page Instead of WP_Error

Maybe sometimes when somebody tried to access your site REST API by /wp-json URL you don’t want the JSON response like this to be displayed:

WordPress REST API is not allowed WP_Error response

There is also possible to display a 404 error page instead:

REST API not allowed 404 page
It is “Storefront” theme by the way, if you don’t recognize it.

In order to do so just replace this line where we return a WP_Error object:

return new WP_Error( 'no_rest_api_sorry', 'REST API not allowed', array( 'status' => 401 ) );

with these lines:

header( 'Content-Type: text/html; charset=UTF-8' );
status_header( 404 );
nocache_headers();
require( dirname( __FILE__ ) . '/404.php' );
die;

Or maybe it will be much easier to just change the default URL of the REST API endpoint.

Change REST API URL

You can also safely change your website REST API URL prefix from /wp-json to whatever you want. For example to /api.

add_filter( 'rest_url_prefix', 'rudr_custom_rest_api_url' );

function rudr_custom_rest_api_url() {
	return 'api';
}

After inserting this code to your website don’t forget to go Settings > Permalinks and just to click “Save Changes” button. It is required to flush the permalink cache.

Remove a custom post type (or a taxonomy) from REST API

It is just a small trick that can be handy in some cases. In order to stop displaying a specific post type or a taxonomy in the REST API we can just provide 'show_in_rest' => false while registering them.

For custom post types:

register_post_type(
	'my_post_type',
	array(
		
		...
		
		'show_in_rest' => false,
	)
);

For custom taxonomies:

register_taxonomy(
	'my_taxonomy',
	array(
		
		...
		
		'show_in_rest' => false,
	)
);

In case a custom post type or a custom taxonomy was registered by a some third party plugin which gets updated from time to time, so we can not hardcode the changes in register_post_type() or register_taxonomy() directly. But we can use a hook: register_post_type_args – for post types or register_taxonomy_args – for taxonomies.

add_filter( 'register_post_type_args', function( $args, $post_type ) {
	
	if( 'my_post_type' === $post_type ) {
		$args[ 'show_in_rest' ] = true;
	}
	return $args;
	
}, 99, 2 );

The register_taxonomy_args filter hook works exactly the same way.

Remove REST API Endpoints

You can remove specific REST API routes just with a rest_endpoints filter hook. For example with the code snippet below we completely remove an endpoint for Users.

add_filter( 'rest_endpoints', 'rudr_remove_rest_api_endpoint' );
function rudr_remove_rest_api_endpoint( $rest_endpoints ){
	
    if( isset( $rest_endpoints[ '/wp/v2/users' ] ) ) {
        unset( $rest_endpoints[ '/wp/v2/users' ] );
    }
    if( isset( $rest_endpoints[ '/wp/v2/users/(?P<id>[\d]+)' ] ) ) {
        unset( $rest_endpoints[ '/wp/v2/users/(?P<id>[\d]+)' ] );
    }
    return $rest_endpoints;
	
}

There are also a plenty of others:

  • /wp/v2/media,
  • /wp/v2/types,
  • /wp/v2/statuses
  • /wp/v2/taxonomies,
  • /wp/v2/tags,
  • /wp/v2/comments,
  • /wp/v2/settings,
  • /wp/v2/blocks,
  • /wp/v2/posts,
  • /wp/v2/pages,
  • /wp/v2/search,
  • /wp/v2/categories.

I listed only those endpoints which support GET requests (i.e just accessible from the browser without authentication).

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X