Constraining use of the Back button

I’m working on a site that has constraints on use of the browser’s Back button. The easiest one to explain is that the user can’t be allowed to go Back to the payment page because she could accidentally pay twice. There are other, similar constraints that are more difficult to explain, and correspondingly difficult to avoid without a generalized way to control when and how the user can move Back.

This is a common problem in web site design; it must affect any site which performs operations that can’t be repeated without side effects.

My question is: what best practices exist for dealing with this issue? For example, are there any applicable design patterns that I could adopt?

BTW, I solved the problem, but it’s ugly. I ended up writing spaghetti code and tweaking it until it handled all of the cases right. It’s the first time I’ve done that since Ronald Reagan was a governor, and the first time I’ve ever done it in a structured language. The experience is not one I care to repeat!

Use POST, after submitting you then redirect (this is important). You no longer need to worry about the user pressing the back button.

Perhaps I’m missing something here. It appears to me that this approach would merely return the user to the current page when she clicks Back. She can still violate the constraint by using the history menu to back up two or more pages.

Constraints can be required for security reasons (generally meaning “cheating” rather than “gaining access to confidential data”). Thus it’s not enough to create a barrier that makes the user less likely to violate a constraint by accident; it’s necessary to create one that prevents her from violating a constraint on purpose.

Yet would do nothing because the POST data would not be submitted again. If it was a GET request from the beginning it would submit again. Using POST and redirection solves the problem you outlined in your initial post without messing with the users back button. When an operation on a web sites makes changes on the server one should use POST and redirection. When retrieving data without making changes one should use GET. POST is destructive, GET is not. Make sense?

Constraints can be required for security reasons (generally meaning “cheating” rather than “gaining access to confidential data”). Thus it’s not enough to create a barrier that makes the user less likely to violate a constraint by accident; it’s necessary to create one that prevents her from violating a constraint on purpose.

Those issues you solve on the server side. Trying to control the user’s back button is unreliable, requires the user to run your client-side code and there is no guarantee the browser will even honor it. It does not solve your “security constraints”.

I appreciate your effort to help, but I think you’ve misinterpreted my question.

My question is: what best practices exist for dealing with this issue?

“Those issues you solve on the server side” doesn’t answer the question; it simply begs the question. We were only talking about the client side because you brought it up.

The solution I have is largely on the server side. I’m not going to give an account of the complications – it would take hours and fill pages – but distinguishing and dealing correctly with all of the different cases, such as new page loads, Back loads, refresh loads, and new loads in different tabs – turned into a nightmare. If there’s a clean, orderly way to do that, I hope to learn about it.

Fine. What is that you are doing that requires such “constraints”?

  • Bringing up client side is a given, the back button resides only on the client-side and can only be controlled with client-side code. So of course it was brought up! I also already told you the best practice, not to mess with the users back button and use POST then redirect. After that using the back button returns the user to the form and not the submit URI the form used.

I hope my last post didn’t strike you as rude. I really do appreciate you taking the time to respond. I think I see the problem differently than you do, and it requires a different type of solution than you’re proposing. I find that frustrating.

I would rather not try to explain the site’s constraints at this point, because a complete explanation would be long, and would take hours to write – and to read. A simplified one would probably not provide enough information to be useful.

I’m trying to approach this in terms of general principles: many types of processes have steps which cannot repeated if they are to yield a correct (or honest, or meaningful) result. A web site that implements such a process may have to prevent the user from doing things that invalidate the result. Depending on the process, the site may require “guard rails” that prevent naive users from making simple mistakes, or it may require strong security to prevent knowledgable users from subverting the process. (I just happen to be working on a site that’s closer to the “strong security” end of the spectrum).

Having taken a step down the road toward security, I found myself caught in a swamp. If the user goes back several pages, how do I tell where they came from and send them back there? If they open a new browser tab and restart the process in it while the process is still under way in an older tab, they can upset the process in a technical sense, instead of merely producing an inaccurate or dishonest result. How can I detect that situation and prevent it? And so on.

Perhaps the key to this is in your statement that if the user violates the constraint by using the history menu to back up two or more pages, that “would do nothing because the POST data would not be submitted again.” Perhaps I misunderstood what you mean.

Here’s how I understood it: the user is on page A, and POSTs to page B, which processes the data and redirects to page C. The user enters some data and POSTs to page D, which process the data and redirects to page E. Now the user opens the history menu and returns to an earlier page.

If she returns to page D, e.g. by clicking Back, the script doesn’t process the data again, because it doesn’t get the data. It just bounces her back to page E.

But in my experience, that’s not so. Page D gets the same data when the user Backs to it as it did when she originally loaded it via POST from page C. I have direct experience with this – it was part of the nightmare I experienced on Spaghetti Street.

If it were so, any Back to a page that follows a POST would fail unless the page was specially coded to deal with that situation, because it wouldn’t get the data it was expecting.

Suppose the user Backs to page C? Page B redirected to page C after processing the data from page A, so C doesn’t expect to get any data (and, a far as I can tell, would get what it expected if it did). What’s to stop page C from collecting data and POSTing to page D, which repeats its first-time processing because it is being loaded by POST, not Back? If it can tell it was loaded by Back instead of POST, how can it tell where the user came back from? It can’t just load the next page in sequence, which might or might not already have been loaded itself.

As with backing to page D, so with page B. As with backing to page C, so with page A.

It’s not clear how the site can deal with this cleanly and generally, whether it does so on the server side, the client side, or a combination of both.