Somebody was inquiring on a mailinglist why he would have a lot of errors in his *-event.log that looked like:
06/26 14:47:27 error Cannot create cookie: domain = .xxx.yyy
Amidst a number a theories a link to a blog post No Cookie for You!! surfaced as an explanation. An explanation that couldn’t possibly be true though (for reasons I will go into later this post). So now I was interested and I set out to get to the bottom of this problem on the commute home.
Cookies
In HTTP cookies are based on 2 headers send between the server and the browser. First, when the server wants to send a cookie, it uses a Set-Cookie header:
Set-Cookie: foo=bar; path=/; domain=.xxx.yyy; expires Mon, 09-Dec-2002 13:46:00 GMT
The Set-Cookie has a name-value pair with the name of the cookie and the value, and a bunch of attributes that allow the browser to determine whether or not to send the cookie back to the server on a subsequent request. And when a browser wants to send a cookie back to a server, it sends a HTTP Cookie header:
Cookie: foo=bar
So what is going on
There are 2 problems with the eplanation provided in No Cookie for You!!
- the cs(Cookie) column in the webserver log does not describe the Set-Cookie header send from the server to the browser, but the Cookie header send from the browser to the server (always remember when reading HTTP logs: cs means client-to-server, sc means server-to-client). So instead of a (failed) attempt to set a cookie, it is actually is proof that the browser sends a cookie back;
- not allowing a cookie to be set is meaningless in terms of the HTTP protocol. HTTP is a request-response protocol: the browser does a request, the server sends a response and that is it, there is no more communication. So even if the browser doesn’t like the cookie, it has no way to communicate that back to the server.
With this in mind I set out to reproduce the problem. First I constructed my own set of HTTP headers to send to the webserver:
GET /index.cfm HTTP/1.1
Host: localhost
Cookie: name=r2; expires=Tue, 02-Jul-2007 02:02:02 GMT;
Sending this set of headers to the webserver over telnet indeed produced an error in the *-event.log:
07/03 18:25:56 error Cannot create cookie: expires = Tue, 02-Jul-2007 02:02:02 GMT
So I set out to make a minimal reproduction. Expecting that the problem was somehow caused by improper escaping of the colons in the timestamp I tried to progressively reduce the number and complexity of the cookies until I ended up with:
GET /index.cfm HTTP/1.1
Host: localhost
Cookie: expires=T;
This still produced an error. Then I tried:
GET /index.cfm HTTP/1.1
Host: localhost
Cookie: e=T;
And I didn’t get an error anymore. And that is when I realized what was going on: the problem isn’t in the value or the escaping of the value, but in the name. Expires is treated as a reserved word because it is an attribute of a Set-Cookie header. Duh!!!
Reserved names
To complete the circle I ran a test with the cfccokie tag:
<cfcookie name="expires" value="test" />
And there I got the pretty grey and blue error message:
Cookie name EXPIRES is a reserved token
Further tests confirm that the following are all reserved names for cookies:
- secure
- domain
- expires
- path
And whenever CF receives a request with a cookie with one of those names, you get a line in your *-event.log with an error. I wonder if we can get CF to ignore that error without polluting the logfile.