I'm trying to do a delete action in custom plugin which uses a custom table and extends wp_list_table for showing the data. The problem is, whenever I try to delete the data I get cannot modify header information and I don't know how to fix it. The code:

    function process_bulk_action() {

    //Detect when a bulk action is being triggered
    if ('delete' === $this->current_action() ) {
        // In our file that handles the request, verify the nonce.
        $nonce = $_REQUEST['_wpnonce'];

        if ( !wp_verify_nonce( $nonce, 'bsp_delete_student' ) ) {
            die( 'Invalid security check!' );
        else {
            self::delete_student( absint( $_GET['student'] ) );

            wp_redirect( esc_url( add_query_arg() ) );

    // If the delete bulk action is triggered
    if ( ( isset( $_POST['action'] ) && $_POST['action'] == 'delete' )
         || ( isset( $_POST['action2'] ) && $_POST['action2'] == 'delete' )
    ) {

        $delete_ids = esc_sql( $_POST['delete'] );

        // loop over the array of record IDs and delete them
        foreach ($delete_ids as $id ) {
            self::delete_student( $id );

        wp_redirect( esc_url( add_query_arg() ) );

I then have a function for deleting:

    function delete_student($id){
    global $wpdb;
        [ 'students_id' => $id ],
        [ '%d' ]

When I comment out the nonce check (which is not working, always gives me the "Invalid security check!") and try to delete a student I get the cannot modify header information and the selected row is not deleted. Any suggestions?

  • 1
    Are you sure that there is no output before the wp_redirect call? Even a blank space at the end of a file? Any output of any kind will trigger PHP to send output headers, and once they're sent, they're sent, you can't undo it and send more. Since redirects work by sending headers, you can't have any output beforehand
    – Tom J Nowell
    Commented Feb 10, 2016 at 16:47
  • What's in the code that displays the form with the nonce field in? Commented Jul 24, 2016 at 8:45

3 Answers 3


You cannot redirect inside process_bulk_actions() call, by the time Wordpress is processing that file headers were already sent ( do not know exactly why ). You should simply write a message, and use wp_die( $message ) instead of die().

When you use bulk actions Wordpress redirects that action to the file showing the list, and that file is going to show your message only if you use wp_die() within process_bulk_actions().

Also, if you want to use the normal actions ( not bulk ) like Edit | Delete, you can use something like this before creating the list object and calling prepare_items():

[Note this code is not in the list class file, but in the file using it, the file that shows the table]

// modify yourslug to fit what you defined in construct 'singular' item
// modify my-edit-file.php with the name of the file which is going to handle
// your edit or delete action.
// $itemId will be received and used by my-edit-file.php

    if(isset($_REQUEST['action']) && $_REQUEST['action'] === 'edit'){
        $itemId = filter_input(INPUT_GET, 'yourslug', FILTER_VALIDATE_INT);

        require_once(plugin_dir_path(__FILE__) . 'my-edit-file.php');
$table = new MyObjectListTable();

Try following code.

add_action('init', 'start_buffer_output');
function start_buffer_output() {
  • This works. I get no errors. But it doesn't delete the row and redirects me to the wrong page. If I remove the wp_redirect it all works fine. No errors and it deletes the row. The nonce still not working.
    – dea
    Commented May 25, 2015 at 12:32
  • It's better to pin down the problem code that's triggering sending the headers so it doesn't cause other problems at a later date. Commented Jul 24, 2016 at 8:44

try wp_safe_redirect() see if it works for you, it does the sanitization for you

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.