When using the expires
flag, the date must be exactly in the format you are currently using, as well as in the GMT
(Greenwich Mean Time) timezone. The reason that your cookie does not get expired 2 minutes after it has been created is because you are using datetime.now(), which returns the current local date and time.
Thus, for instance, if your current local timezone is GMT+2
and time is 20:30:00
(hence, GMT
time is 18:30:00
), creating a cookie that expires at 20:32:00 GMT
will actually tell the browser to delete this cookie in 2 hours and 2 minutes (from the time it has been created). If you look at the cookie’s Expires / Max-Age
column in your browser’s DevTools (e.g., on Chrome, go to Network
tab in the DevTools, click on the request’s name and then on the Cookies
tab), you will notice a Z
at the end of the datetime, which means UTC
(Coordinated Universal Time)—that is, an offset from UTC of zero hours-minutes-seconds. You can check the response headers as well, where you can see the cookie’s expires
flag set to 20:32:00 GMT
. There is no noticable time difference between UTC
and GMT
(if you would like to learn more about their differences, have a look at this post).
Hence, you could either replace .now()
with .utcnow()
in your code:
from datetime import timedelta, datetime
def get_expiry():
expiry = datetime.utcnow()
expiry += timedelta(seconds=120)
return expiry.strftime('%a, %d-%b-%Y %T GMT')
or use time.gmtime(), passing as secs
argument the time.time() (which returns the time in seconds) plus the desired lease time (in seconds):
import time
def get_expiry():
lease = 120 # seconds
end = time.gmtime(time.time() + lease)
return time.strftime('%a, %d-%b-%Y %T GMT', end)
For either the above two methods, use:
cookie['expires'] = get_expiry()
You can also use the undocumented way of passing the expiry time directly in seconds instead. For instance:
cookie['expires'] = 120
An alternative to expires
is the max-age
flag , which specifies the cookie’s expiration in seconds from the current moment (similar to the above way). If set to zero or a negative value, the cookie is deleted immediately. Example:
cookie['max-age'] = 120
Note:
If both expires
and max-age
are set, max-age
has precedence (see relevant documentation on MDN).
Also, as per RFC 6265:
4.1.2.1. The
Expires
AttributeThe
Expires
attribute indicates the maximum lifetime of the cookie,
represented as the date and time at which the cookie expires. The user
agent is not required to retain the cookie until the specified date
has passed. In fact, user agents often evict cookies due to memory
pressure or privacy concerns.4.1.2.2. The
Max-Age
AttributeThe
Max-Age
attribute indicates the maximum lifetime of the cookie,
represented as the number of seconds until the cookie expires. The
user agent is not required to retain the cookie for the specified
duration. In fact, user agents often evict cookies due to memory
pressure or privacy concerns.NOTE: Some existing user agents do not support the Max-Age attribute. User agents that do not support the Max-Age attribute ignore the attribute.
If a cookie has both the
Max-Age
and theExpires
attribute, the
Max-Age
attribute has precedence and controls the expiration date of
the cookie. If a cookie has neither theMax-Age
nor theExpires
attribute, the user agent will retain the cookie until “the current
session is over” (as defined by the user agent).
Also note, as mentioned in MDN documentation regarding the expires
flag:
Warning: Many web browsers have a session restore feature that will save all tabs and restore them the next time the browser is used.
Session cookies will also be restored, as if the browser was never
closed.
Another thing to note is that, since Sep 2022, Chrome limits the cookie’s max-age
to 400 days:
When cookies are set with an explicit
Expires/Max-Age
attribute the
value will now be capped to no more than 400 days in the future.
Previously, there was no limit and cookies could expire as much as
multiple millennia in the future.
Using FastAPI/Starlette
It should also be noted that FastAPI/Starlette provides an easier way to set cookies on the Response
object, using the set_cookie
method, as described in this answer. As per Starlette documentation:
max_age
– An integer that defines the lifetime of the cookie in seconds. A negative integer or a value of0
will discard
the cookie immediately.Optional
expires
– An integer that defines the number of seconds until the cookie expires.Optional
Example from FastAPI documentation:
from fastapi import FastAPI, Response
app = FastAPI()
@app.post("https://stackoverflow.com/")
def create_cookie(response: Response):
response.set_cookie(key='token', value="token-value", max_age=120, expires=120, httponly=True)
return {'message': 'success'}