Yesterday, as I was working on my Scotch On The Rocks (SOTR) ColdFusion Framework presentation, I discovered a huge error in my understanding of the way in which cookies can be set in ColdFusion. For years, I thought the difference between the CFCookie tag and the Cookie scope was that only the CFCookie tag caused the given cookie to be sent to the client where as the cookie scope was just a regular struct. Meaning, you could use the cookie scope to store arbitrary values; but, if you wanted to send cookies to the client, you needed to do so with the CFCookie tag.
I don't know when or how I formulated this belief but, as I found out yesterday, it happens to be completely wrong. As it turns out, both the CFCookie tag and the Cookie scope send cookies to the client. The only real difference between them is that CFCookie gives you more control over how the cookies are sent to the client. When you use the Cookie scope directly to set a cookie, it creates a session cookie (which expires when the browser is closed) for the current domain. CFCookie, on the other hand, gives you the ability to control all aspects of the cookie including its domain, pathing, and secure page usage.
To demonstrate this, I set up this snippet of code:
<!--- Set a cookie with CFCookie. ---> <cfcookie name="setWithCFCookie" value="true" expires="never" /> <!--- Set a cookie directly with COOKIE scope. ---> <cfset cookie.setWithCookie = true />
As you can see, I am setting cookies using both the CFCookie tag as well as the raw Cookie scope. When we run this code, we see the following page response activity in Firebug:
As you can see, both approaches sent cookie headers to the client; the only difference was that the cookie set via the Cookie scope has no expiration date (session cookie).
In order to do this, the Cookie scope has to be special; it doesn't constantly re-send all the available cookies - it only sends the cookies that were created in the current request. As such, I figure that the cookie scope must have some sort of implicit setter methods that "listen" for property updates. And, when they detect that the cookie scope has been programmatically mutated, it knows to add additional Headers to the response. Out of curiosity, I dump'ed out the underlying Java class and got this:
Clearly, the cookie scope is not a standard ColdFusion struct (coldfusion.runtime.Struct). This must be how it knows to set additional response headers as the name-value cookie pairs are set. Luckily, as my pervious view on cookies was "stricter" than necessary, it never caused unexpected behaviors. But, now that I know that I don't need to use the CFCookie tag all the time, direct use of the cookie scope will be a very nice and easy way to create session cookies.
Want to use code from this post? Check out the license.
Thank you for this post. I currently work for a company that uses ColdFusion to build web apps. I'm not sure, if my colleagues know about this. Now I have a link to point them to. Thx again.
Chat with you later!
I've been using ColdFusion for years; it's a little embarrassing that I didn't know about such a fundamental behavior of the language. I figured if I didn't understand it then there might be others who didn't get it either.
didn't know that either, thanks for the post!
Cool - glad I could help. I've been misinformed on this for so long, I can't even remember how I formed my previous understanding. I could swear I was taught this way.
You don't have to be embarrassed. Nobody knows everything :-)
I've checked Abode Livedocs, because I wondered what Adobe has to say. The behavior is documented, but I haven't noticed it until your post.
So... thanks again :-)
I appreciate the kind words.
Very interesting piece of knowledge to store away for the future....
Ben, I just made this "discovery" myself about two weeks ago. Like you I thought the only way to send set-cookie headers was using cfcookie, but I was obviously wrong. I wonder if this behavior changed at some point?
Anyway, one other thing I will add to this is that using StructDelete() on the cookie scope will also send set-cookie headers. For example, StructDelete(cookie,"setWithCookie") will send the following headers:
Set-Cookie SETWITHCOOKIE=;expires=Sun, 03-May-2009 14:55:02 GMT;path=/
Notice ColdFusion clears the value and sets the expiration date to a date in the past.
Great. I was wrong too, though that's the same meaning :D
Thank Ben, it's absolutely small discovery but big meaning to us :D
Oh nice! I didn't even think to test the structDelete() usage on cookie. I guess that makes sense though (from a consistency standpoint). I really feel like this must have changed at some point; or, I was simply taught incorrectly at some point.
Excellent - glad to help clarify.
Any one know how to set a cookie for multiple domain extentchins - domainone.com , domainone.ca, domainone.in
Thanks for posting this. I used to get a lil confused between these two approaches and at some point pushed it to the side thinking that it's just 2 ways of accomplishing the same thing.
Evidently not! It's good to know the subtle but important distinction. Thanks again!
I don't believe that that is possible; I'm pretty sure that would be considered a security concern. But, beyond the COOKIE scope, my understanding is not so thorough.
No problem at all. When in doubt, I just tend to use CFCookie to set values. The only time I use the Cookie scope to set a value is when I am CFParam'ing it; and, that is only when I don't need it to last that long. Since they are session cookies (no expiration date - what happens when you set the cookie value directly in the scope), they only last for the duration of the browser.
I believe to set the cookie for multiple domains you use multiple <cfcookie>'s changing the domain for each one in your list.
Hey Ben, I just wanted to say thanks for the info, I just started working with cookies in a CF site and this helps.
One question though, is it possible to update the cookie's value (noob question I know :P) But basically when a user comes to my site, a cookie called "Cookie_UID" is created and given a value of 0 (just a generic UserID). Then once a specific user logs in, I want to update that "Cookie_UID" to be the user's actual UserID. I can post my code here if you'd like, but I didn't want to fill up too much space :)
Thanks again for the info!
Ben, I've garnered much help from your various posts about CF. I need a tiny bit of specific guidance.
I have a small CF application running and have set a few cookie scope variables under a struct concept (cookie.LNet.x, cookie.LNet.y, etc) during the onSessionStart process.
I am able to display these cookie values by cfouput. I then tried to pass the cookie struct to a component/method using cfinvokeargument (sending #cookie.LNet#) into a struct variable. During this invocation, I get an error message that LNet is undefined in cookie.
I rewrote a little and found I can access the cookie directly within the component/method.
So, is there any reason to try to pass the cookie through the invokeargument like I would any other struct? Is it good form to access the cookie directly from within the component/method?
@Ben (, @Dafydd)
We just experienced this extremely weird problem.
1) Log in to the site
2) See that SESSION.UserID was set
3) See that another SESSION.var was set
4) Go to 2nd page. The two previous SESSION variables remained
5) Go to 3rd page...Gone!
Turns out SetDomainCookies="Yes" needed to be in the application.cfm in the CFApplication line (we're in CF9, but code is pretty old now).
We're still testing, but basically we think our cookies were conflicting with a completely different server's CF Cookies because they are on the same domain. Talk about maddening!
Here's the Adobe Doc pertaining to the above: