Request for Comments: Cookie Max-Age attribute
- Version: 1.1
- Date: 2012-12-28
- Author: Andrey Andreev firstname.lastname@example.org
- Status: Implemented
- First Published at: http://wiki.php.net/rfc/cookie_max-age
A proposal to utilize the Max-Age cookie attribute.
As already described by the title, this RFC proposes that setcookie(), setrawcookie() and ext/session must send the Max-Age attribute alongside Expires when a Set-Cookie header is created.
Why is it needed?
Currently, there is only one factor that directly affects a user agent's decision of when a cookie should be expired - the Expires attribute. And that would've been fine, if there was a guarantee for both of the following conditions:
- User agents always parse the timestamp correctly.
- User agents always have correct time settings.
Since user agents will calculate the difference based on their own timestamp and the one that they receive, if any of the above two conditions isn't met, then we'll have a problem - the cookie will expire either earlier or later than expected. I can speak from experience, but I'm sure all of you would also understand and agree that this problem is very hard to debug in the rare cases where a client reports it.
Always sending a UTC-based timestamp should give us enough confidence to consider the first condition to always be satisfied. The second one however is never guaranteed and even though we can always blame the problem on a client-side configuration issue - why not just solve the problem once and for all, since a solution is available?
How does it help?
Unlike Expires where a full timestamp is sent, the Max-Age attribute provides a time difference value in seconds (shortly called “TS delta”). A TS delta eliminates the need of the above mentioned conditions, since user agents no longer need to make complex (compared to it) parse and calculation operations.
Instead, they will now need to just add the number of seconds to their local time in order to determine the cookie expiry time. It doesn't matter if they have the right time settings, because that works even for the wrong ones (and it would be more accurate, too!).
Technical details and considerations
- A zero (0) value means that the cookie must be immediately dropped (as should any negative value).
- The Max-Age attribute is unfortunately, not supported by all user agents. This is natural, since it is relatively new.
- User agents that don't support it should ignore it (as with any other unrecognized cookie attribute).
- User agents that do support it must give it a higher precedence and will practically ignore Expires.
The above list makes it pretty clear that the behavior of any user agent that can't take advantage of Max-Age won't be affected. With that said, I believe that it is both safe and proper to send both Expires and Max-Age at the same time, as this will provide a proper fallback for any “legacy” browser that doesn't support Max-Age.
Since setcookie() and setrawcookie() accept a Unix timestamp - the TS delta will have to be calculated based on the provided timestamp, like this:
<max-age> = <provided unix timestamp> - <current time>
ext/session on the other hand will directly send the configured session.cookie_lifetime value, which is exactly the same - TS delta, in seconds.
Possible BC effects
No downside is expected, except the few more bytes being sent in HTTP headers.
Assuming our current time is: Fri, 28-Dec-2012 03:00:00 GMT
setcookie('name', 'value', 1356663605); // Old header: // Set-Cookie: name=value; expires=Fri, 28-Dec-2012 03:00:05 GMT // New Header: // Set-Cookie: name=value; Expires=Fri, 28-Dec-2012 03:00:05 GMT; Max-Age=5
setrawcookie('name', 'value', 1356663595); // Old header: // Set-Cookie: name=value; expires=Fri, 28-Dec-2012 02:59:55 GMT // New Header: // Set-Cookie: name=value; Expires=Fri, 28-Dec-2012 02:59:55 GMT; Max-Age=-5
session_name('PHPSESSID'); session_set_cookie_params(3600, '/', 'domain.tld'); session_start(); // Old header: // Set-Cookie: PHPSESSID=<random session id>; expires=Expires=Fri, 28-Dec-2012 04:00:00 GMT; path=/; domain=domain.tld // New header: // Set-Cookie: PHPSESSID=<random session id>; Expires=Expires=Fri, 28-Dec-2012 04:00:00 GMT; Max-Age=3600; Path=/; Domain=domain.tld
- Pull request on GitHub (I am not an experienced C coder, please feel free to improve or write an alternative)
- 2012-12-28: Updated with suggestions from the thread on PHP-Internals.
- 2012-12-28: Initial version.