How to prevent page from resubmit on refresh when “Cannot modify header information – headers” warning shows

So the root problem is sending headers, specifically the timing.

Webpages have http headers, and http headers come before the content. The headers tell the browser what it’s recieving, and they must be sent first.

When PHP outputs stuff, it needs to tell the browser to expect output, so the moment a character is output, PHP sends HTTP headers telling the browser to expect output.

Once they’re sent, they’re sent. Horse is out the stable door, cats out of the bag, they can’t be changed.

So by the time your site reaches your shortcode, it’s already output a html element, and a header and a title etc, it’s too late to send a redirect header!

As a result we can conclude, that redirecting inside a shortcode, is not possible. Another solution is required

The root problem is that you’re trying to do things inside a shortcode. Displaying the output in the shortcode is great, but it’s not good for actual processing.

So instead, lets add a hidden input to the form that we can use to pick up on the form submission:

<input type="hidden" name="form_submitted" value="true" />

Then look for that value on the init hook:

add_action( 'init', function() {
    if ( ! empty( $_POST['form_submitted'] ) ) {
        deliver_email();
    }
}

And finally, fix the broken shortcode that outputs directly instead of returning a HTML string:

function cf_shortcode() {
    ob_start();

    html_form_code();

    return ob_get_clean();
}

Additional notes:

  • prefix everything, but pick one more unique than cf
  • You don’t need to include the PHPMailer files, wp_mail and its filters will do fine
  • There’s no exit statement after your header redirect
  • Use wp_safe_redirect instead of header
  • Don’t hardcode the URL, there are functions for that, e.g. home_url() etc