111

Page one contains an HTML form. Page two - the code that handles the submitted data.

The form in page one gets submitted. The browser gets redirected to page two. Page two handles the submitted data.

At this point, if page two gets refreshed, a "Confirm Form Resubmission" alert pops up.

Can this be prevented?

iwein
  • 25,788
  • 10
  • 70
  • 111
Emanuil Rusev
  • 34,563
  • 55
  • 137
  • 201
  • Use a confirmation dialog: http://stackoverflow.com/questions/6457750/form-confirm-before-submit/12357337#12357337 –  Sep 11 '12 at 12:08
  • 3
    use post redirect get as described here -->> http://en.wikipedia.org/wiki/Post/Redirect/Get – Meer Oct 17 '14 at 11:52
  • You can prevent that even without redirection. Look [here](https://stackoverflow.com/a/47247434/4632019) – Eugen Konkov Nov 12 '17 at 11:51
  • 1
    Possible duplicate of [How to prevent form resubmission when page is refreshed (F5 / CTRL+R)](https://stackoverflow.com/questions/6320113/how-to-prevent-form-resubmission-when-page-is-refreshed-f5-ctrlr) – Eugen Konkov Nov 12 '17 at 11:52

12 Answers12

151

There are 2 approaches people used to take here:

Method 1: Use AJAX + Redirect

This way you post your form in the background using JQuery or something similar to Page2, while the user still sees page1 displayed. Upon successful posting, you redirect the browser to Page2.

Method 2: Post + Redirect to self

This is a common technique on forums. Form on Page1 posts the data to Page2, Page2 processes the data and does what needs to be done, and then it does a HTTP redirect on itself. This way the last "action" the browser remembers is a simple GET on page2, so the form is not being resubmitted upon F5.

CodeTwice
  • 2,926
  • 3
  • 18
  • 18
  • 2
    A variant of method 2 is a redirect to another page. For example, you POST to http://example.com/save.html and once the save is done, you redirect to http://example.com/list.html. – Guillaume Oct 22 '10 at 11:10
  • 1
    With Method 1, I'd use the jQuery plugin named BlockUI to let the client knows something's happening. – Shikiryu Oct 22 '10 at 15:58
  • I am using method 2 but it's asking me to resubmit again If I refresh it. Anyone having similar problem to this ? – keen Dec 30 '13 at 07:44
  • Are you sure that the HTTP redirect is working properly? Open the network inspector / firebug / whatever your browser has for inspecting outgoing HTTP requests and check out the HTTP calls. You should see the first one (posting the form data) happening with the POST method, returning HTTP code 301 with Location header pointing to itself, and then immediately you should be seeing another HTTP query to the same page with GET method. – CodeTwice Dec 30 '13 at 09:14
  • @CodeTwice: In my case, page1 posts to page2, page2 processes the data and shows a page(with values being passed to the template)..Where should the redirect happen?..The page does not have a GET requests. – user1050619 May 07 '14 at 19:08
  • @user1050619 In such a case, page1 should post to page2, page2 should process the data but instead of returning actual HTML (from the template) it should either do a HTTP redirect on itself using GET method, or - if it does not accept GET requests - do a HTTP redirect to page3 which does take GET requests. – CodeTwice May 12 '14 at 07:42
  • If the update is slow, user can still hit page refresh before we've performed redirect, so AJAX is the only foolproof method. – Ondrej Svejdar Oct 23 '15 at 12:42
  • Cant use both, My GET and POST url is same. When URL accessed with GET it displays page1 and when it accessed with POST it displays page2. If I redirect to self, Page2 will be changed to page 1. – Muhammad Saqib Aug 20 '18 at 17:42
27

You need to use PRG - Post/Redirect/Get pattern and you have just implemented the P of PRG. You need to Redirect. (Now days you do not need redirection at all. See this)

PRG is a web development design pattern that prevents some duplicate form submissions which means, Submit form (Post Request 1) -> Redirect -> Get (Request 2)

Under the hood

Redirect status code - HTTP 1.0 with HTTP 302 or HTTP 1.1 with HTTP 303

An HTTP response with redirect status code will additionally provide a URL in the location header field. The user agent (e.g. a web browser) is invited by a response with this code to make a second, otherwise identical, request to the new URL specified in the location field.

The redirect status code is to ensure that in this situation, the web user's browser can safely refresh the server response without causing the initial HTTP POST request to be resubmitted.

Double Submit Problem

Double Submit Problem

Post/Redirect/Get Solution

Post/Redirect/Get Solution

Source

Eugen Konkov
  • 22,193
  • 17
  • 108
  • 158
Angelin Nadar
  • 8,944
  • 10
  • 43
  • 53
  • 2
    Good explanation. I am wondering what will happen if the user clicks the back button on the browser after the redirect is made. Will the "confirm form resubmission" popup show again. I guess even if the confirmation popup doesnt show again , a post request on page 1 with the same data be made again and once again the user will be redirected to page 2. I have a series of forms , form1 , form2 , form3. how can go back from form"n" to form"n-1" seamlessly. Random question: Where did you study the PRG design pattern – Rpant Oct 07 '15 at 03:37
  • @Rpant on chrome the php file that sends the location header doesn't even show up in the browser history, so the back button simply takes you back to the form. – FluorescentGreen5 May 05 '17 at 12:03
  • @Angelin After redirect how get will get the data? For ex when I post some data to /some_url and redirect to /some_url which makes a GET request. How do I know what should be the response as /some_url is generic and doesn't conatins an id like /some_url/? – Amit Tripathi Aug 06 '17 at 06:08
  • @AmitTripathi you have to create yourself. eg: POST request could be of inserting customer data whereas GET would be display of the data inserted successful. You will be able to show the data as it was nothing but the form data or re-use the view of the customer page by fetching from database for that customer by the customer id you received after successful insertion into database. – Angelin Nadar Jul 29 '20 at 17:56
  • But I agree with @Eugen Konkov solution – Angelin Nadar Jul 29 '20 at 17:59
  • The [solution](https://stackoverflow.com/a/47247434/4632019) is here. – Eugen Konkov Jul 30 '20 at 10:09
10

Directly, you can't, and that's a good thing. The browser's alert is there for a reason. This thread should answer your question:

Prevent Back button from showing POST confirmation alert

Two key workarounds suggested were the PRG pattern, and an AJAX submit followed by a scripting relocation.

Note that if your method allows for a GET and not a POST submission method, then that would both solve the problem and better fit with convention. Those solutions are provided on the assumption you want/need to POST data.

Community
  • 1
  • 1
davin
  • 44,863
  • 9
  • 78
  • 78
9

The only way to be 100% sure the same form never gets submitted twice is to embed a unique identifier in each one you issue and track which ones have been submitted at the server. The pitfall there is that if the user backs up to the page where the form was and enters new data, the same form won't work.

Blrfl
  • 6,817
  • 1
  • 25
  • 25
6

There are two parts to the answer:

  1. Ensure duplicate posts don't mess with your data on the server side. To do this, embed a unique identifier in the post so that you can reject subsequent requests server side. This pattern is called Idempotent Receiver in messaging terms.

  2. Ensure the user isn't bothered by the possibility of duplicate submits by both

    • redirecting to a GET after the POST (POST redirect GET pattern)
    • disabling the button using javascript

Nothing you do under 2. will totally prevent duplicate submits. People can click very fast and hackers can post anyway. You always need 1. if you want to be absolutely sure there are no duplicates.

iwein
  • 25,788
  • 10
  • 70
  • 111
5

You can use replaceState method of JQuery:

<script>
   $(document).ready(function(){
   window.history.replaceState('','',window.location.href)
   });
</script>

This is the most elegant way to prevent data again after submission due to post back.

Hope this helps.

Rohan Rao
  • 2,505
  • 3
  • 19
  • 39
2

If you refresh a page with POST data, the browser will confirm your resubmission. If you use GET data, the message will not be displayed. You could also have the second page, after saving the submission, redirect to a third page with no data.

Joel
  • 2,654
  • 6
  • 31
  • 46
1

Well I found nobody mentioned this trick.

Without redirection, you can still prevent the form confirmation when refresh.

By default, form code is like this:

<form method="post" action="test.php">

now, change it to <form method="post" action="test.php?nonsense=1">

You will see the magic.

I guess its because browsers won't trigger the confirmation alert popup if it gets a GET method (query string) in the url.

Highlight
  • 124
  • 4
0

The PRG pattern can only prevent the resubmission caused by page refreshing. This is not a 100% safe measure.

Usually, I will take actions below to prevent resubmission:

  1. Client Side - Use javascript to prevent duplicate clicks on a button which will trigger form submission. You can just disable the button after the first click.

  2. Server Side - I will calculate a hash on the submitted parameters and save that hash in session or database, so when the duplicated submission was received we can detect the duplication then proper response to the client. However, you can manage to generate a hash at the client side.

In most of the occasions, these measures can help to prevent resubmission.

ehe888
  • 158
  • 2
  • 6
0

I really like @Angelin's answer. But if you're dealing with some legacy code where this is not practical, this technique might work for you.

At the top of the file

// Protect against resubmits
if (empty($_POST))  {
   $_POST['last_pos_sub'] = time();
} else {
     if (isset($_POST['last_pos_sub'])){
        if ($_POST['last_pos_sub'] == $_SESSION['curr_pos_sub']) {
           redirect back to the file so POST data is not preserved
        }
        $_SESSION['curr_pos_sub'] = $_POST['last_pos_sub'];
     }
}

Then at the end of the form, stick in last_pos_sub as follows:

<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>
Scott C Wilson
  • 19,102
  • 10
  • 61
  • 83
0

Try tris:

function prevent_multi_submit($excl = "validator") {
    $string = "";
    foreach ($_POST as $key => $val) {
    // this test is to exclude a single variable, f.e. a captcha value
    if ($key != $excl) {
        $string .= $key . $val;
    }
    }
    if (isset($_SESSION['last'])) {
    if ($_SESSION['last'] === md5($string)) {
        return false;
    } else {
        $_SESSION['last'] = md5($string);
        return true;
    }
    } else {
    $_SESSION['last'] = md5($string);
    return true;
    }
}

How to use / example:

if (isset($_POST)) {
    if ($_POST['field'] != "") { // place here the form validation and other controls
    if (prevent_multi_submit()) { // use the function before you call the database or etc
        mysql_query("INSERT INTO table..."); // or send a mail like...
        mail($mailto, $sub, $body); // etc
    } else {
        echo "The form is already processed";
    }
    } else {
    // your error about invalid fields
    }
}

Font: https://www.tutdepot.com/prevent-multiple-form-submission/

0

use js to prevent add data:

if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
Tran Anh Hien
  • 687
  • 8
  • 11