Overview I’m mot going to do a deep dive into cookies and their use in the web on the whole. If you think an introduction like that would help you understand this post better, here is one place you might start.
The very high-level that will apply to what we’re going to talk about here is this:
Each request
object made available in a Django view has a COOKIES
attribute, which returns a dictionary containing key value pairs, both of which are strings.
So you could access the value of a specific cookie by running:
cookie_val = request.COOKIES[cookie_name]
Unfortunately, browsers don’t send additional information about the cookies, such as when it expires or which domain it was set on. If you need access to this information within your application logic, you will need to include it in the value of the cookie itself.
To tell a browser to set a cookie, you call ‘set_cookie’ on the HTTP response object before returning it. The key
is required and the value
defaults to an empty string (sometimes the presence or absence of a cookie as all you need to know, making the value irrelevant). All other keyword arguments are optional, and are explained in the Django documentation if you’re unsure how to use them!
Calling set_cookie
with the key
of a cookie that already exists will overwrite the existing cookie, effectively replacing it.
Beware! Because the COOKIES
attribute of a request
is a mutable dictionary, it can be tempting to add or edit values in it. And while something like request.COOKIES[‘new_key’] = ‘new_value’
won’t throw an error…it also won’t have any impact. Browsers set cookies based on HTTP responses they receive, so cookies need to be set on the response object for the browser to process them.
Just like setting a cookie, Django has a function called delete_cookie
that can be used for this purpose, and takes a subset of the arguments passed into set_cookie
.
This seems straightforward, but there are a few things that can trip you up, and cause cookies to not actually be deleted. According to the docs:
Due to the way cookies work, path and domain should be the same values you used in set_cookie() – otherwise the cookie may not be deleted.
This is interesting! Let’s dig into this, just a little. If you look at how Django has implemented delete_cookie
, you’ll see this;
def delete_cookie(self, key, path='/', domain=None, samesite=None):
self.set_cookie(key, max_age=0, path=path, domain=domain, samesite=samesite, expires='Thu, 01-Jan-1970 00:00:00 GMT')
This de-mystifies the need to use the same attributes that were used to set the cookie, to delete the cookie. Browsers don’t have the concept of “deleting” a cookie. There’s only telling the browser “this cookie is expired now.” Which means that if you don’t use the same attributes that were used to set the cookie originally, instead of expiring your existing cookie, it will create a new one that we’ll never see because it’s immediately expired.
The addition of widespread browser support (and enforcement, in the case of Chrome) for SameSite cookies is a fairly recent addition. (If you’re unsure about how or when to use the SameSite attribute on cookies, I found this to be a very comprehensive and approachable overview). Django versions 2.1 and newer provide a samesite
keyword argument for setting and deleting cookies. However, even if you’re running on an older version of Django than that, you may still need to apply this logic to be in compliance with more recent browser updates.
It’s a little less clean than using the Django-provided keyword argument in newer versions, but the good news is, your response object has a cookies attribute that you can use to set additional attributes on your cookie. Like so:
response.cookies[cookie_name]['SameSite'] = <“none”/”lax”/”strict”>
This gets a little more complicated when we talk about deleting cookies. Similar to the requirement that cookies be deleted with the same path
and domain
attributes with which they were created, you may not be able to delete a cookie without the correct SameSite
value. As we’ve established, if you’re running an older version of Django, you don’t have access to this via the built in delete_cookie
function. To get around this, you’ll need to implement your own deletion, by running set_cookie
with a max_age
of 0, and then manually setting the SameSite
attribute, as we did above.