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:

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:

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

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
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