Lecture on Cookies

State

The underlying way that browsers and servers interact (HTTP: the hypertext transport protocol) is stateless. What that means is that the server doesn't remember that it has interacted with your browser before. The second time, tenth, millionth time is just the same as the first time.

This is convenient for implementing web servers, but it's inconvenient for users and web applications. Having state allows much better experiences for users.

What do we mean by state? Anything that depends on the history of your interactions with the website:

One of the oldest and still most important solutions to providing stateful interactions on the web is cookies. A cookie is a like a ticket from a dry cleaners: just as you hand the ticket to the clerk and expect to get back your own garments, the browser hands the cookie to the server along with the request for the web page, and it can get back a customized page.

Today, we'll learn about cookies and how to use them to do interesting things.

The Request-Reply Model

In the HTTP protocol (the P in HTTP), the browser and the server talk to each other to get/send web pages. A typical request (from browser to server) looks like this:

GET /index.html HTTP/1.0 Accept: text/html, image/gif, image/jpeg Accept-Language: en

Typical reply (from server to browser):

HTTP/1.0 200 OK
Date: Fri, 20 Apr 2010 16:20:00 GMT
Server: Apache/1.3.9 (Linux)
Content-Length: 141
Content-Type: text/html

<html>
<body>
<p>The web page stuff.
</body>
</html>

Cookies

Cookies live on the client

What's in your cookie jar? Take a couple of minutes to poke around in your browser to find the cookies. These things move from version to version, but as of this writing, you can find cookies:

Sharing Cookies

Since cookies live on the client, what does that mean in terms of privacy and security? Here are a few common scenarios:

A Schematic of the Request-Reply Model, with Cookies

cookies are sent to the browser along with the web page

  1. Browser sends a request to the server. Since the user has never visited that site before, there are no cookies, so none are sent.
  2. The server notices that there are no cookies, so it assumes that this is a new visitor, and it generates a new ID for the user and includes that ID as a cookie in the reply. The reply might even be tailored for new visitors.
  3. The browser later (minutes or months later, but before the cookie expires) makes another request at that site and sends back the cookie it got in step 2.
  4. The server gets the request with the cookie, realizes this is a returning visitor, and can generate a customized reply.

Using JavaScript with Cookies

The document returned by the server might have some JavaScript in it, and the JavaScript code can also look at the cookie and customize things. The JavaScript code can also set cookies. This allows you to have some of the benefits of cookies with static HTML pages or a server CGI script that doesn't use cookies. The picture looks almost the same:

cookies can also be set by JavaScript

Here are some things we might remember in a cookie:

Let's look at some of these possibilities. All of them will be based on two JS functions: setCookie and getCookie. These functions are not built-in to the JavaScript language. Earlier, we learned how to load JavaScript code from an external file; these functions are defined in a file in the CS110 account: http://cs.wellesley.edu/~cs110/js/cookies.js. You would load that library file in the head of your web page, so that the functions will be defined before they are used.

Using the power of abstraction, we won't study the code of those functions, though it's within your skills. (See below if you're curious.) All you really need to know about the functions is the following:

function setCookie(cookie_name,value,expire_days,path) ... function getCookie(cookie_name) ...

Before we start using these functions, though, let's take a brief detour that will help us test our use of these functions.

Showing Cookies

We could always look up the document's cookies using one of the techniques that we saw in the cookie inspection exercise, but that can get tedious. Since JavaScript can look at the document's cookies, I've implemented the little box fixed at the upper right that will let you set, show or clear the document's cookies whenever you like.

Try setting a cookie using the form and then displaying it. You can also see it in more detail by inspecting the browser's cookie jar.

setCookie()

To set a cookie from JavaScript code, you can use the setCookie() function defined in the cookies.js file. This function has two required arguments: the name and value of the cookie (both strings).

The next argument, expire_days, is optional. If you omit it, a cookie is set that expires when the browser closes. If you supply that argument, it's the number of days that the cookie should last (an integer).

The path argument is also optional. You can use it to restrict the pages within the site that share the cookie. You might set it to "/" if you want every page in your domain (e.g. cs.wellesley.edu) to match; that would be the least-restrictive option. You'll see that in most of the cookies in your browser. On Puma, you could set it to "/~youraccount/" to restrict the cookie to just your account. That means that another student whose web pages are also on Puma, would not be able to read or modify the cookies associated with your pages. For that reason, you should probably restrict it as much as reasonable, just to avoid inadvertently interfering with cookies for other pages.

Example: The following code sets the cookie named "zip_code" to a value gotten from the user, default value of 02481. It will expire in a week. Try it, and look at the values in the cookie jar!


getCookie()

The second function, which is also defined in cookies.js, is much easier. It takes just one argument, the name of the cookie (a string) and returns its value, also a string. If there is no such cookie, the function returns the empty string.

Example: The following code gets the cookie named "zip_code," if any.

var zip = getCookie("zip_code");

Noticing First-Time Visitors

The following text is dynamically written based on the value of a cookie:

Suppose we want to do something special for a first-time visitor, but not for return visits. Maybe a special introduction or greeting, as we did in the green box at the top of this section. How can we tell whether someone has visited this page before? We could set a cookie!

We might set a cookie named "first_time" with a value of "no." Also, we have to consider that for a first-time visitor, no cookie will be set, so we have to notice the absence of the cookie. Your cookie code should always be aware of the possibility that there may be no cookie at all.

The code is here:

/* Code to remember whether someone has visited before.  
   Relies on cookies.js

   Scott D. Anderson
   Fall 2009
*/

// 1. Try to get the cookie
var first_time = getCookie("first_time");
// 2. respond appropriately
if ( first_time == "no" ) {
    document.writeln('Welcome back!');
} else {
    document.writeln('We hope you enjoy your first visit!');
}

// 3. Set it for the next visit. (Expires in a month)
setCookie("first_time","no",30);

Let's talk through the code briefly:

  1. We try to get the cookie; it might be the empty string.
  2. If it has the value of "no," then it must've been set on some prior visit, and so this is a return visit. If this is a first visit, the first_time variable will have a value equal to the empty string.
  3. Based on the outcome of that condition, we respond appropriately.
  4. Last, we set the cookie, so that it'll be set for the next visit

Note that we put that code (inside script tags or loaded from an external file using the script tag) exactly where in the body of the page we want the text to appear. We don't put that code into the head.

Try it! Try reloading this page to see the effect of the cookie. You can also try clearing the cookies and reloading.

Here's that idea in a separate web page distinguishing first visits from returns. View the source and look for the two script elements; everything else is irrelevant.

Websites like Amazon.com, Facebook, and many others work like this, only instead of just recording a value like "no," they also (or instead) save some unique identifier, which is usually a very long string of gibberish, and they use the unique identifier to look up your name and account info in a database.

Counting Visits

A slight extension of this idea is to count the number of times this person has visited. (Maybe they get a prize after a certain number of visits.) All we have to do is create a cookie that stores the number of visits. Of course, we have to keep in mind that the cookie might not be set at all.

Try it! Try reloading this page to see the effect of the cookie.

The code is here:

/* Code to count visits
   Relies on cookies.js

   Scott D. Anderson
   Fall 2011
*/

// 1. get the cookie
var visits = getCookie("visit_count");
// 2. figure out number of prior visits
var visit_count;
if ( visits == "" ) {
    visit_count = 0;
} else {
    visit_count = parseInt(visits);
}
// 3. respond appropriately
document.writeln("You have visited this site "+visit_count+" times before.");
if( visit_count == 0 ) {
    document.writeln("Thanks for coming!");
} else if (visit_count > 10) {
    document.writeln("Wow, I'm glad you like our site!");
} 

// 4. Increment the counter, expire in a year
setCookie("visit_count",visit_count+1,365);

Here's that idea in a separate web page with visit counter. View the source and look for the two script elements; everything else is irrelevant.

Notice two important facts:

Saving a User's Choice

If we can remember the number of times a user has visited, perhaps we can remember some customization that the user chooses. In this example, we will allow a user to choose her background image. We'll do this in two steps. First, we'll look at how to choose the background image without saving the value in a cookie. Then, we'll add the cookies so that the choice sticks.

Without Cookies

Here's the first version:

Try it: Visit this page w/ image choice.

All of the HTML will be familiar to you. The CSS might be a little new. There is a background-image CSS property that allows you to set a background image for an element. The value is the URL of the image, wrapped in the special url() syntax:

  body { background-image: url('images/flowers.jpg'); }

In JavaScript and the DOM, we can modify that CSS property by assigning to the elt.style.backgroundImage.

Another slight bit of trickiness is that we use a new event handler called onChange. As you might guess, the onChange event handler gets executed when the form value changes — here, that's when we change our menu selection.

Adding Cookies

The second version is almost identical. There are three differences:

Let's see it in action before we look at the code:

Try it: Visit this page w/ sticky image choice.

Saving the cookie is a fairly simple extension of the code in our onChange event handler. We make the value of the cookie be exactly the value that we want to assign to the backgroundImage property.

Reading the cookie is the tricky part — in particular, when to read it. A good choice is to have it run in the onLoad event handler. The onLoad event handler is run when the page finishes loading. It's specified as an attribute of the body tag, and it looks like this:

<body onload="alert('page has finished loading');"> 

In our case, of course, we do the following:

 <body onLoad="document.body.style.backgroundImage=getCookie('bgimage_choice');"> 

This just takes the value of the cookie and assigns it to the style property. Works great!

But what if there is no cookie? In that case, we'll end up assigning the empty string, but that has the effect of unsetting the background image, which works fine.

Saving User Preferences

In an earlier lecture, we learned about how we could create an accessibility bar. That code didn't have any ability to save the font-settings, though, so the user would have to re-choose the settings every time she comes to the page. That's annoying at best.

We will create a simple box with two font-size changers in it. They will allow the user to increase or decrease the font-size. Let's see it in action first

Try it: Try this page that lets you modify the font size

There are a few tricky parts of the code. First, let's look at the underlying JavaScript, which is very short:

/* simple code to increase/decrease font size
   from an initial value of 12px.

   Scott D. Anderson
   scott.anderson@acm.org
   November 2011
*/

var FontSize=12;  // in px

function changeFontSize(diff) {
    FontSize = FontSize + diff;
    document.body.style.fontSize=FontSize+"px";
}

A single global variable keeps track of the current font size. Just one function is defined: it adds its argument to the global variable, and then sets the fontSize style property on the body to the desired font size. This one function takes care of both increasing and decreasing the font size: if called with a positive number, the font gets bigger, and if it's called with a negative number, the font gets smaller. If called with zero, it doesn't change the global variable, but it does set the document's font size to the value of the global variable.

The HTML code looks like this:

<body onload="changeFontSize(0);">

<div id="font_widgets">
  <a href="javascript:changeFontSize(-1);">a</a> &nbsp;
  <a href="javascript:changeFontSize(+1);">A</a> 
</div>
  

Note the odd-looking href parts of the hyperlinks. Because they start with javascript:, these execute the code in the href, instead of taking us to another URL. This is a convenient way to allow your user to interact using links instead of form buttons.

Exercise on Setting Font Size

Improve the example above so that it uses cookies to remember the user's chosen font size. You'll have to:

  1. Save the fontsize.html and fontsize.js files to your local machine. Open them for editing. All your changes will be in the fontsize.js file.
  2. Retrieve a cookie for tracking the font size from the cookie jar. If the cookie is the empty string, the FontSize variable should initialized to 12.
  3. If the cookie has a value, then update the value of the FontSize global variable to the value stored in the cookie.
  4. Modify the fontsize.js file so that whenever the font size is changed, the cookie's value is also changed.

Cookies and Weather

The National Weather Service is good enough to provide a nice forecast site (your tax dollars at work), but it doesn't use cookies to remember your location. (It would have been so easy.)

Here's a page that lets the user show the weather by filling out a form.

The form above allows the user to give her zip code and get the forecast included in her page. Using cookies, have the browser remember her zip code and automatically load the weather forecast.

To do:

  1. Change the file to load the cookies.js library file.
  2. Figure out what you want to name your cookie, since both the getter and the setter need to know that.
  3. Implement code to save the value of the zip code as a cookie (this is the setter). Probably best to implement a function to do this, and invoke that function in the event handler for save zip.
  4. Implement code to look up the value of the zip code from the cookie (this is the getter). Again, it's probably best to implement a function to do this, and invoke that function in the use saved zip button.
  5. For extra credit, put the saved zip code value into the form as the default value, so the user can see it.
  6. For extra extra credit, implement a checkbox saying "remember this" next to the zip code, and only save the cookie if the user checks that box.

You could imagine getting the cookie in an onLoad event handler, so that if there is a cookie, the page will automatically load the weather forecast for that place, without the user having to ask.

Legitimate Uses

There are many legitimate uses for cookies, and most modern websites can't adequately function without them, so they're really indispensable for features such as:

Less legitimate uses

Nevertheless, there can be a dark side to cookies:

DoubleClick

DoubleClick was a company founded in 1995 that managed to use cookies to track individuals' behavior across websites. Websites that used DoubleClick's ads included absolute links to DoubleClick's ad server, which could then set a cookie as it responded to the image request. The cookie for ad.doubleclick.net could then track which websites the user visited, and this allowed the ads to change based on users' browser behavior. Google eventually acquired DoubleClick in 2008.

From the W3C Security FAQ, some more information about cookies and their security risks.

Summary

A cookie is just a small amount of data, saved on your local computer, that the browser will send back to the server whenever you visit that site. That allows the server to recognize you and, possibly, provide a customized response. The cookies can also be read and written by JavaScript code.

A condensed version of these notes can be found here. See a demonstration of a modified version of Wendy Wellesley's home page that counts the number of times the user has visited the page.

Further Information and Examples

Beyond here is information that we think you might find interesting and useful, but which you will not be responsible for. It's for the intellectually curious student.

Saving Form Data

Some of the examples above asked the user for some information (such as their name) using prompt(). Of course, we can also ask them for information using forms. This means that we can save the data in a form using cookies.

Why would we want to do this? Often websites don't want to process incomplete or partial forms, and saving partial forms could be a hassle. However, they could easily allow the data to be saved using cookies. Many drafts could be saved this way, with zero burden on the server. When the user is finally happy with the form, they submit it as usual.

Here's an example of a saveable form. The code is a little outside what we've covered in this course, but just a little. If you'd like to do something like that for your site, talk to your advisor.

There are a few tricks to the cookie functions that we have been using, but only a few, so the essentials of the code are reasonably understandable. If you're curious, read the code for ../../cookies.js.

Solutions