Prevent Posts in the Trash from Being Updated via REST API
This is a very specific post so it may not be useful for many of you guys, but on the other hand I didn’t even find I hint about anything like this across the internet, so, for those of you who need it, this tutorial is a gem. I hope 🙂
Let’s say that you have my Simple WP Crossposting plugin installed on your main site (Site 1) or it could be any custom functionality or a plugin that uses a post update REST API endpoint {WP url}/wp-json/wp/v2/posts/{post ID}. So basically we’re syncing changes between the same posts on “Site 1” and “Site 2”.
And our goal is to prevent syncing changes we made on “Site 1” when a post copy on “Site 2” has been moved to the trash. Please note that here I mean not only restoring a post from the trash (changing the post status) but any changes.
We can go different ways here:
- We can send an additional REST API request before updating a post to check whether it is in the trash on “Site 2” or not.
- Or we can use a simple code snippet on “Site 2” to just prevent the post from being updated in case it is in the trash.
Of course the 2nd way wins in performance! So we’re going to use it. But you can use the first way of course if you wish, in that case before every REST API update request you need to send an extra GET request to /wp/v2/posts?slug={your post slug}.
Right now we need a hook before posting via REST API! And I decided to use rest_pre_insert_{post type}. If you think that there is a better hook for that, please let me know in the comments section below.
// for Posts
add_filter( 'rest_pre_insert_post', 'rudr_prevent_trashed_post_updates' );
// for Pages
add_filter( 'rest_pre_insert_page', 'rudr_prevent_trashed_post_updates' );
// repeat it for any custom post type you want it to use
function rudr_prevent_trashed_posts_updates( $prepared_post ) {
// do nothing if we're not updating (but publishing) a specific post
if( ! isset( $prepared_post->ID ) ) {
return $prepared_post;
}
if( 'trash' === get_post_status( $prepared_post->ID ) ) {
return new WP_Error( 'misha_err2', 'Updating posts in the trash is not a good idea.' );
}
return $prepared_post;
}Ok, it is time to deconstruct the code above:
- The hook
rest_pre_insert_{post type}runs almost right beforewp_insert_post()in the REST API controller and I think it is the best option of the hook to use. - The
IDproperty of a$prepared_postobject doesn’t exist when we’re publishing a post, not updating it, so I am using a simpleisset( $post->ID )to check it. And as you remember we don’t have to do anything when a new post is being created. - Also note that
$prepared_postobject is not aWP_Postobject, so we can not simply pass it into theget_post_status()function, that’s why I am passing only$prepared_post->ID.
And when sending a request like this and getting an error we can process it the following way:
if( 200 !== wp_remote_retrieve_response_code( $response ) ) { // 500 by the way
$response_body = json_decode( wp_remote_retrieve_body( $request ), true );
print_r( $response_body );
/*
Array
(
[code] => misha_err2
[message] => Updating posts in the trash is not a good idea.
[data] =>
)
*/
}Prevent Trashed and Private Posts from Being Updated via WordPress REST API
This is an interesting example because if you’re just about to add a private post status to check into the previous code snippet, be ready to get an error like this on the “Site 2” every time you update your private posts:

So, that’s how our new code snippet is going to look like for both trashed and private posts:
add_filter( 'rest_pre_insert_post', 'rudr_prevent_trashed_and_private_post_updates', 25, 2 );
function rudr_prevent_trashed_and_private_post_updates( $prepared_post, $request ) {
// do nothing if we're not updating (but publishing) a specific post
if( ! isset( $prepared_post->ID ) ) {
return $prepared_post;
}
// let's check the User-Agent header
if( false === strpos( $request->get_header( 'user_agent' ), 'rudrastyh.com' ) ) {
// do nothing if not a REST API request from the "Site 1" (in this case not from rudrastyh.com)
return $prepared_post;
}
if( in_array( get_post_status( $prepared_post->ID ), array( 'trash', 'private' ) ) ) {
return new WP_Error( 'misha_err3', 'Private posts and posts in the trash are not allowed for updates.' );
}
return $prepared_post;
}Some notes:
- Don’t know where to use this code? Please check this guide.
- Don’t forget to replace the domain on line
11with your own domain of the “Site 1” (it is the site you’re going to crosspost from). User-Agent header is usually looks this way:WordPress/WP_VERSION; URL(for example WordPress/5.0; https://rudrastyh.com) and we just check the URL in it.
Misha Rudrastyh
Hey guys and welcome to my website. For more than 15 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