1

Based on research here and elsewhere, I have the following code that sets a cookie on a site:

add_action( 'init', 'my_set_cookie',1 );

function my_set_cookie() {
    if (! $_COOKIE['mycookie']) {
       $guid = 'xxxx'; // normally a real guid value so it will be unique
       setcookie('mycookie', $guid, time()+(3600*24*30),'/');
    }
return;
}

But even though the init hook is used, and with a priority of '1', I still get 'headers already sent' error for the 'setcookie' statement. Using another priority (say '99') also gives the error.

Is there a different hook to use to set the cookie?

7
  • Are you absolutely sure there's no prior output? A stray space or tab in a plugin file? A PHP warning? The moment a single character of output is made, PHP has to send headers, after which it's too late. Once they're sent there's no way to go back and add/change things. It could be as simple as a plugin with a closing tag and a space character at the end of a file. Messing with the priority on the add_action call will have no effect, as it should have worked to begin with
    – Tom J Nowell
    Jan 6, 2019 at 2:05
  • Good idea. The error message indicates that the theme's header.php has started the output. So does that mean that the theme is loaded before the init hook can fire? Is there a hook that can be used before the theme is loaded - perhaps that's the solution? Jan 6, 2019 at 3:57
  • Theme is loaded before init is fired... Jan 6, 2019 at 8:34
  • @RickHellewell where is the code snippet from your question? If header.php has already been sent then this is way too late for the init hook which will have been fired long before that. Which file is this in? And what's the full error message? Usually headers being sent is referring to the HTTP headers, nothing to do with WordPress' header.php, this is a general PHP thing, not something WordPress itself is complaining about
    – Tom J Nowell
    Jan 6, 2019 at 13:42
  • This code is in my plugin that is activated. The error message indicates that output was already sent by the theme's 'header.php'. The error line number is the output of the '<!DOCTYPE html>' before the '<head>' statement. The plugin has to set a cookie, so the 'init' hook was used, as recommended in numerous places here and elsewhere. I tried using the 'muplugins_loaded' hook, which is first on the list from the link from the answer by Krzysiek Dróżdż. So, what is the recommended way for a plugin to set a cookie? rather use a cookie, not a session var, as the cookie needs to last a year. Jan 6, 2019 at 23:16

4 Answers 4

4

It’s a problem as old as WP, or even older...

You can’t set cookies, or send any other headers, if any of the content of the site is already sent.

You try to set cookies using init hook. And it is OK in most cases, since no output should be printed yet. But... Not all code is written correctly.

There are many other thing done before init. Take a look at actions run during a typical request.

As you can see... Themes get loaded before init hook. And so do plugins.

So if their code cause any warning/notice (and the debug is on) or they generate any other output, then you’ll get error while setting cookie.

Another popular cause for this problem is using closing PHP tags (?>) at the end of files. If there is any white characters after that closing tag, then it’s treated as output also.

1

Here is another post to this topic with some suggestions on solving this: https://stackoverflow.com/questions/50205548/setcookie-not-working-wordpress-because-of-header

I used send_headers hook. But it would be great if someone can elaborate more, if this is really the correct hook to use for setting cookies.

1

You can solve this issue by checking if the header is already sent:

headers_sent()

function cc_set_cookie() {
    if(!headers_sent())
        setcookie('COOKIE_NAME', 'COOKIE_VALUE', time()+31556926, '/');
}
add_action('init', 'cc_set_cookie');
4
  • 2
    That's solving the symptom though, not the cause. And you'll fail to set the cookie.
    – Rup
    Jan 1, 2021 at 0:32
  • @Rup That's wrong I'm able to set a cookie with this :) If you are using this approach your cookie won't get set? Jan 1, 2021 at 20:26
  • If the headers have been sent then the cookie won't get set, no, because your code doesn't call setcookie then. And if the headers haven't been sent then the headers_sent() check won't make any difference. The best solution would be to work out how you can reliably run this code before the headers have been sent. If you have to test headers_sent then something has gone wrong.
    – Rup
    Jan 2, 2021 at 12:36
  • Yes, I understand your point however I think this is the case. The init hook will get called several times so only at the moment where the header isn't sent it will set the cookie. If it runs later (after the header was set) it won't set a cookie. That's how this fixes the warning issue. You could probably do something like to check if the function was called already.However it's working: Try it out :D Jan 3, 2021 at 15:34
0

After trying various hooks to try to set the cookie before the theme loaded it's header.php file (none of the hooks worked, even those very early in the 'load cycle'), I figure that I might temporarily disable the error so that the cookie can be set. I based my answer on this question.

Now, I realize that this is not a standard way of doing it, and probably not recommended, but my justification is that if a theme is not doing things 'normally' or 'correct', and therefore causing the 'headers already sent' error message to display on the screen, then quickly bypassing that error display (and then restoring whatever error trapping settings are in place) is a solution that will work in my situation.

It appears from my testing that even if 'headers already sent' happens, the cookie is set properly. That doesn't really sound like it should work, but it appeared to work in my testing.

So, the technique I used is

  • get the current error settings to be used later

  • turn off display of errors

  • do things that cause the error (in my case, set the cookie)

  • restore the error settings to original valuess

Here is the code that I used. Again, probably not 'kosher', but it worked in my case.

// some themes cause a 'headers already sent' message, so we bypass it here
// by turning off that error message while we set the cookie.
// see https://stackoverflow.com/a/26154061/1466973
//  $random_guid is a guid set previously
$previousErrorLevel = error_reporting();
error_reporting(\E_ERROR);
setcookie('cookie_name', $random_guid,time()+(3600*24*30),'/');
error_reporting($previousErrorLevel);

Since it worked for my case, I am marking my answer as correct, although the other answer is valid. And I expect a couple of downvotes, as this is not kosher. But it worked for my problem and circumstances.

Your Answer

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

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