For auditing issue, I only want a post can be trashed, but not deleted - for all users.

So I have a plugin like

add_action('before_delete_post', function($id) {

But seems not all delete action are ajax, so it will be show a black screen with return an error page with result "0"

Feature wise the above code is ok, but are there any better way?

3 Answers 3


Don't let the action die, just do a redirect (to wherever you'd like):

function wpse_92155_before_delete_post() {
} // function wpse_92155_before_delete_post
add_action('before_delete_post', 'wpse_92155_before_delete_post', 1);
  • Qui old, I know, but how this work with AJAX calls and XML-RPC requests? I don't think that in these cases, redirection is the right solution. Commented Jul 18, 2014 at 8:33

I would use the hook before_delete_post as the last layer of protection against deletion (using @tf's solution, and which is the correct Answer).

And first remove the "Delete" options from sight. The following hides the Bulk Actions and the Empty Trash button, and removes the Delete Permanently row action.


With this code:

add_action( 'admin_head-edit.php', 'hide_delete_css_wpse_92155' );
add_filter( 'post_row_actions', 'hide_row_action_wpse_92155', 10, 2 );
add_filter( 'page_row_actions', 'hide_row_action_wpse_92155', 10, 2 );

function hide_delete_css_wpse_92155()
    if( isset( $_REQUEST['post_status'] ) && 'trash' == $_REQUEST['post_status'] ) 
        echo "<style>
            .alignleft.actions:first-child, #delete_all {
                display: none;

function hide_row_action_wpse_92155( $actions, $post ) 
    if( isset( $_REQUEST['post_status'] ) && 'trash' == $_REQUEST['post_status'] ) 
        unset( $actions['delete'] );
    return $actions; 

This is the result:


Note of interest

There is no hook cpt_row_actions. The hooks page_row_actions and post_row_actions are applied if the post type is hierarchical or not, respectively.

  • Mkay. +1 to both you and @tf. I couldn't upvote his one-liner before, because further information pending I could have only assessed its quality by further research on the subject. Yet another answer thoroughly put into context. Commented Mar 25, 2013 at 0:31

This is an old post, but for others still seeking an answer, I think there's a more elegant way – filtering user_has_cap. This allows you to remove the users rights to delete a post under whatever circumstances you determine. Filtering user_has_cap automatically removes the UI elements and the action in one simple function and works no matter how the attempt to delete is made. See this post for more details. 

add_filter ('user_has_cap', 'wpcs_prevent_deletion', 10, 3);
function wpcs_prevent_deletion ($allcaps, $caps, $args) {
    if (isset($args[0]) && isset($args[2]) && $args[0] == 'delete_post') {
        $post = get_post ($args[2]);
        // Insert your logic here
        if ($prevent_deletion) {
            $allcaps[$caps[0]] = false;
    return $allcaps;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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