Detecting Reload and Back Browser Actions

Created 2006-11-25 / Edited 2006-11-25

Tags: HTTP, Back Button, Web Application Structure

Detecting when a user uses their Reload or Back buttons in a web application isn't very difficult, and the payoff in useability can be quite large. One of the most-heard complaints about web applications is back-button brokenness.

Disable Caching

This first step towards detecting a press of the back-button is to disable client side caching. When a browser caches the page, the back-button will merely display the cached version and the server is never notified. When the user then uses the cached page, the application and the user can become confused about what data is being manipulated.

While adding meta tags to HTML is helpful, the best way to stop caching is right in the HTTP headers. I like to add:

Cache-Control: private, no-store, no-cache
Pragma: no-cache
Expires: 0

which gets the job done. I've also seen the Expires: header set to some date in the distant past, which also works.

Assign PageID To Each Output

Once you have caching disabled, you should next assign a unique Page Identifier (PageID) to each page the application outputs. I generally try to make this as short as possible, and generally use Hexidecimal or Base-64 to fit the ID in less characters.

Also attach this PageID to each input back into the program. Add it as a GET parameter on each link, and as a hidden input for each form. It is ugly but necessary. This will allow us to know which page generated which program input -- in other words, which page the user was looking at when they send a given request.

Each PageID should be added to the session information so that we can track what pages have already been displayed.

Use PageID to Detect Reload / Back

With the PageID mechanism in place you have enough information to detect browser Reload and Back Button actions. Now, with every input sent by the user to the application, the PageID will indicate whether the user is requesting a page they've already seen.

If the recieved PageID is the one we just sent out, then the user pressed Reload.

If the recieved PageID was generated before the most recent one, then the user pressed the Back button (or otherwise returned to an old page).

Avoiding The Re-Post Prompt

One thing that I've annoyingly run into is the result of a user pressing the Back button after submitting a form via POST. Most browsers, following the standards and being ever so helpful, prompt to ask whether the data should be re-POSTed. This is because, well, generally it is a bad idea to re-POST data.

We, however, don't mind so much since it can be detected.

After a bit of research, it seems the best method to overcome this prompt is by doing a redirect right after the POST using the 303 HTTP Status. The 303 Status means "See Other" and does just what we want -- it redirects the user transparently without prompting them or caching the redirect. This status code was created for just this purpose as part of HTTP 1.1 -- if you are working with a pre-1.1 browser you may want to send Status 302 instead.

The Downside

Having a PageID as part of the GET or URL in each request is ugly. Worse (or perhaps as an elaboration on the truth behind the first statement), it is a meaningless number for users and can only cause them pain. When the user bookmarks or copy/pastes the URL the PageID goes along too, forever attached.

This is very anti-REST, but of course this whole thing technique is pretty anti-REST :)

Make sure your application does the Right Thing for requests with unrecognised PageIDs, and expires the PageIDs (removes them from the list of used PageIDs) quickly.