7

I am using ASP.NET MVC (latest version).

Imagine having 2 pages:

Page-1: "Enter data" >> Page-2: "Thank you"

After submitting Page-1 you are being redirected to Page-2.

My goal: I want to make sure that you can't go back to Page-1 when you hit the browser's back button once you made it to Page-2. Instead I want you rather to stay on Page-2 (or being pushed forward to Page-2 every time you hit the back button).

I have tried all different kind of things. The following is just some simplified pseudo code ...

[NoBrowserCache]
public ActionResult Page1(int userId)
{
   var user = GetUserFromDb(userId);
   if (user.HasAlreadySubmittedPage1InThePast)
   {
      // forward to page 2
      return RedirectToAction("Page2", routeValues: new { userId = userId });
   }

   var model = new Page1Model();

   return View("Page1", model);
}

[NoBrowserCache]
[HttpPost]
public ActionResult Page1(Page1Model model)
{
   var user = GetUserFromDb(model.UserId);
   if (user.HasAlreadySubmittedPage1InThePast)
   {
       // forward to page 2
       return RedirectToAction("Page2", routeValues: new { userId = model.UserId });
   }

   // Save posted data to the db
   // ...

   return RedirectToAction("Page2", routeValues: new { userId = model.UserId });
}

public class NoBrowserCache : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Haha ... tried everything I found on the web here: 
        // Make sure the requested page is not put in the browser cache. 
        filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        filterContext.HttpContext.Response.Cache.SetNoStore();
        filterContext.HttpContext.Response.Cache.AppendCacheExtension("no-cache");
        filterContext.HttpContext.Response.Cache.SetExpires(DateTime.Now);
        filterContext.HttpContext.Response.Expires = 0;
    }
}

If only I could make sure that a request is being sent to the server every time I hit the back button. But right now, clicking the back button just pulls Page-1 from my browser's cache without sending a request to the server. So currently, I have no chance to redirect you forward to Page-2 by server means.

Any ideas or suggestions?

Thanks, guys!

Btw: There is no login/authentication involved. So, I can't use Session.Abandon() or stuff like this. And I would rather use some server based code than javascript if possible.

EDIT 2017-5-12 Following @grek40, I made sure that the anti-chaching statements end up in the browser. I therefor completely removed the [NoBrowserCache]-ActionFilterAttribute from my C# code above. Instead I added the following statements in the <head> section of my _Layout.cshtml:

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

I confirm that these three lines are being rendered to my browser (used my browser's developer tools to inspect). However, caching still works. I can still move backward without any server requests. Tested this with Google Chrome v62, Firefox Quantum v57 and Microsoft Edge v41 (all on Win10). #

EDIT 2017-6-12 Again following @grek40's suggestions: tried Expires: 0 as well as Expires: -1. No difference. I still didn't manage, to turn off my browser's cache.

enter image description here

disinfor
  • 10,865
  • 2
  • 33
  • 44
Ingmar
  • 1,525
  • 6
  • 34
  • 51
  • For the moment, lets just assume that Page 1 is fully cached in the client browser and that browser decides to serve the cached page without doing any kind of server query. You are out of the game. It's your responsibility to handle client requests to the server, not controlling the client display details. So basically I assume there is some Post-action in Page 1 that is problematic when the user posts twice. If so, you have to prevent double-posts (tell the user his input was ignored due to a previous post) instead of preventing the previous page display. – grek40 Dec 04 '17 at 15:35
  • What is your goal here? I assume it really isn't "can't go back to page 1" and from the pseudo code I'm guessing it is "make sure a user can only submit the data once." You need to adopt the "never trust input" principle and validate the form on submission. You can't 100% prevent the page from being served up from the browser cache. – Colin Young Dec 04 '17 at 15:37
  • 1
    While you can't enforce any client behavior, you can of course give some hints to the browser, that you don't want client side caching. The following might be relevant: https://stackoverflow.com/a/918346/5265292 – grek40 Dec 04 '17 at 15:38
  • Guys, thank you for your replies so far. @grek40 and Colin Young: I totally agree. Of course I am doing server validation at all important steps. So, I am not worried about a 2nd post back. This will lead to exactly what you guys suggested: "You already submitted your data." In my application's context the goal is rather to let the user know and feel that there is "no way back" by *not letting him go back*. It's not critical if he does go back, but I would rather keep him on Page-2. – Ingmar Dec 04 '17 at 18:33
  • @grek40: for your second post (including the link): this seems to be exactly what I tried to achieve with the ActionFilterAtrribute in my code sample. Doesn't help though. The browser cache still knows Page-1. So, whatever I tried, in the end the back button will bring me back (without firing a server request). – Ingmar Dec 04 '17 at 18:35
  • Can you please edit your question and confirm that your page really included all the suggested anti-cache mechanisms (check the resulting headers and meta tags client side!). Even if you include all those things in your code, your webserver may decide to add/modify some metadata before shipping the page, so this is going nowhere until we know whether the browser ignore the settings or the settings are never transferred. – grek40 Dec 04 '17 at 21:58
  • Thanks @grek40. Just updated my question. Still no success though. Probably still missing something ... – Ingmar Dec 05 '17 at 07:22
  • @Ingmar I'm not yet convinced that you really inspected everything... please refer to https://stackoverflow.com/a/33974626/5265292 for headers that are part of the http response but not part of the html head. Some remark on `Expires`: https://stackoverflow.com/q/11357430/5265292. Basically I'm out of ideas if the HTTP headers are not at fault. – grek40 Dec 05 '17 at 07:31

5 Answers5

5

Finally I found a solution. It's javascript based, but very simple.

I just had to add the following snippet to my Page-2 (the page I don't want users to leave anymore once they got there):

<script type="text/javascript">

$(document).ready(function () {
    history.pushState({ page: 1 }, "title 1", "#nbb");
    window.onhashchange = function (event) {
        window.location.hash = "nbb";
    };
});

I found this here: how to stop browser back button using javascript

Thanks to all your support guys. But "my own" solution was the only one that worked.

Ingmar
  • 1,525
  • 6
  • 34
  • 51
  • 1
    What about when they have Javascript disabled? Is that a concern? – sab669 Dec 07 '17 at 15:53
  • @sab669: nope, not a real issue. I am very well aware, that my solution is not safe in every aspect. Going back using the browser history menu is still possible. But that's not a problem. I just wanted to prevent the obvious (browser back button). Thank you though. – Ingmar Dec 07 '17 at 19:30
  • Although blocking user navigation is typically considered bad form, it is often necessary in a 'wizard-style' site. This solution works great with no screen flash. – Jason Marsell Sep 03 '18 at 15:36
2

This can be done by using javascript. Use the following code

     <script type = "text/javascript" >
   function preventBack(){window.history.forward();}
    setTimeout("preventBack()", 0);
    window.onunload=function(){null};
   </script>

or check the following link1 and link2.

1

This little piece of code might help you in solving your issue.

<script type="text/javascript">
                /*To retain on the same view on Back Click*/
                history.pushState(null, null, window.location.href);
                window.addEventListener('popstate', function (event) {
                    history.pushState(null, null, window.location.href);
                    event.preventDefault();
                    });
</script>
Kumar_Vikas
  • 837
  • 7
  • 16
0

You can add a line of javascript to every page for a client-side solution:

history.forward();

See docs on MDN. When there is a page to go forward to (which is when the used pressed the BACK button), this will force the user to that page. When the user already is at the most recent page, this does nothing (no error).

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
Hans Kesting
  • 38,117
  • 9
  • 79
  • 111
  • thanks so much for your suggestion as well. I tried this before (sorry, should have mentioned this in my original post - but I had tried som many other things as well, I couldn't even remember ;)). Anyways, I tried adding this command to my Page-1 one more time. It works nicely on the first time I hit the back button. A short flickering shows me that I was pushed forward to Page-2 again. But when I hit the back button a second time, it *will* bring me back to Page-1 again. So, this isn't an option really :( – Ingmar Dec 04 '17 at 18:39
0
Response.Cache.SetCacheability(HttpCacheability.NoCache);  // HTTP 1.1.
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.
Mike
  • 721
  • 7
  • 13
  • 7
    Thank you for this code snippet, which might provide some limited, immediate help. A proper explanation [would greatly improve](//meta.stackexchange.com/q/114762) its long-term value by showing *why* this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you've made. – Toby Speight Dec 05 '17 at 12:48
  • @Mike: thank you for your code snippet. But as you might have seen in my code sample, I already tried that (see the [NoBrowserCache] attribute). Didn't help though :( – Ingmar Dec 06 '17 at 06:09