I written a plugin to handle login functions. The plugin checks for login info in the_content filter and shows only for logged users. wp_signon shows an error/warning

Warning: Cannot modify header information - headers already sent by (output started at W:\websites\ku\wp-includes\class.wp-styles.php:347) in W:\websites\ku\wp-includes\pluggable.php on line 943

 * Plugin Name: test-plugin
 * Description: my test plugin writing experience in WordPress
 * Version: 1.0
 * Author: KK

function my_add_shortcodes() {
    add_shortcode( 'my-login-form', 'my_login_form_shortcode' );

function my_login_form_shortcode() {
    return ob_get_clean();

function filter_content( $content ) {
        if ( ! empty( filter_input( INPUT_POST, 'login_submit' ) ) ) {
            $login_info = array_map( 'sanitize_text_field', $_POST );
            $email = $login_info['email'];
            $password = $login_info['password'];
            echo 'email '.$email.' password '.$password;
            $user = wp_signon(array('user_login' => $email, 'user_password' => $password, 'remember' => $rememberme), is_ssl());
            if( $user instanceof WP_User){
                echo " check_credentials ".$user->display_name;
                wp_set_current_user($user->ID, $user->user_login);
            } else {
                if( is_wp_error($user)){
                    echo $user->get_error_message();
                    $this->$error = "Email or Password error";
                    return $this->$error;
        } else {
            return "<p>[my-login-form]</p";
        return $content;
add_action( 'init', 'my_add_shortcodes' );
add_filter( 'the_content', 'filter_content' );

1 Answer 1


What you want to do cannot be done in a shortcode.

wp_signon needs to set some HTTP headers for the cookie, but it's too late.

When your browser recieves a webpage, it recieves some headers that tell it what it's recieving and stuff like wether it succeeded or not, is it a 404, what cookies there are etc. Then it has a body which contains the webpage itself, e.g. a JSON string, or a bunch of HTML.

So once the headers are sent, and the body starts, no more headers can be sent.

This isn't a WordPress specific thing, but a universal HTTP/HTML thing. E.g. if you run a python app and send a header to the browser telling it HTTP 200, and the body section has started, that python app can't just add an extra header for a cookie at the end. The server has to respond with a valid HTTP request that follows an expected format for the browser to process, then JS might do some work on the browser side and make extra requests, there's no mutual back and forth that would allow this.

So if you use a shortcode, output has already started, you got your header, and the page title, and the body tag etc etc. That's body output, it needs to send headers to do that, so the very first time any output of any kind, even a blank space is echo'd out, PHP sends the HTTP headers indicating that HTML is on its way.

So if you want to call wp_signon, it has to happen before any output occurs. A shortcode is too late for this.

Shortcodes are meant to allow you to embed things that need PHP to run, they were never intended for this kind of functionality. Sure embed a login form, but the handling can't be done in the shortcode itself, you will need to hook into an earlier hook like init, look at the GET/POST details to see if the user submitted the form, then do all the handling needed to call wp_signon at that point.

  • Your answer is very clear. Thanks for sharing
    – Riskhan
    Aug 2, 2020 at 0:22

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.