You can’t delete more than one cookie at a time (and a couple other cookie bits)

Sometimes you glance at uncommented code and immediately know both what it’s trying to do (good!) and that it’s failing utterly to do it (bad!). Might be existing programmer slang for this, but I’m gonna dub it Error Code UTU8 (i.e. you thought you ate).

Like here in someone’s UTM tracking script:

document.cookie = "utm_campaign=, utm_medium__c=, utm_source__c=, utm_content__c=, utm_adgroup__c=, utm_keyword__c=; gclid__c=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;";

The code above is self-evidently trying to blank out a bunch of cookies that were set individually like so:

document.cookie = "utm_campaign=partner-invite-2024Q2;expires=Sat, 28 Dec 2024 12:34:56 UTC;path=/;"
document.cookie = "utm_medium_c=email;expires=Sat, 28 Dec 2024 12:34:56 UTC;path=/;";
document.cookie = "utm_source_c=blog-newsletter;expires=Sat, 28 Dec 2024 12:34:56 UTC;path=/;";
// etc.

Bad news for the developer: you must run the document.cookie setter separately for each cookie. There are no “batch actions” with cookies.

What the above code is actually doing is setting the cookie named utm_campaign to the value:

, utm_medium__c=, utm_source__c=, utm_content__c=, utm_adgroup__c=, utm_keyword__c=; gclid__c=

then back-dating the value. So the utm_campaign cookie is deleted, but no other cookies are touched.

Here’s the before:

And the after:

N.B. You delete a cookie by setting its expiration date in the past. The value doesn’t actually matter in this case, so utm_campaign= and utm_campaign=whatever with a past expiration have the same effect. The convention is to set to an empty string when deleting, but that’s kinda misleading because an empty cookie value is valid, as long as it’s not expired! (It’s the expires that deletes it, not the fact that it’s empty.)

More bits

While I have your attention, here are a couple more bits of misguided cookie code found in the wild. (These bits, and many more, need to be wrapped up in a grand Falsehoods Martech Devs Believe About Cookies™ post at some point.)

Bit 2: You don’t need to “wait for” changes via document.cookie to take effect, they’re always immediate (synchronous)

Another groanworthy bit is thinking the browser does some delayed/asynchronous business when you set document.cookie.[1] Nope, any change to document.cookie is committed immediately and the cookie store is up-to-date by the next line.

So you never need anything like this:

document.cookie = "utm_campaign=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;";
setTimeout(function(){
  // something that reads from document.cookie
}, 1000);

Generally, such code exists because of another race condition — not directly related to cookies — and people panic and say “maybe the cookie wasn’t set yet?” Even if the real cause is found and fixed, this code exists for eternity, fixing nothing and just causing confusion.

Some browsers (Safari in particular) trust cookies marked HttpOnly more than script-writeable cookies. This leads devs to think they can remove script-writeability from JavaScript, presumably hiding the cookie from code going forward but giving it elevated trust:

document.cookie = "utm_campaign=partner-invite-2024Q2;HttpOnly;expires=Sat, 28 Dec 2024 12:34:56 UTC;path=/;";

This will not work. When HttpOnly is encountered in JS, the browser just goes, “Well, that’s impossible, you’re in JSland so you can’t tell me what do in HTTPland” and deletes the cookie if it exists.

(Only reason you might think this works is because, well, it does hide the cookie from JS, in a morbid sense!)

Notes

[1] There is a new Cookie Store API (not the document.cookie setter) that’s asynchronous, but it’s not yet widely supported.