31

Sometimes the user may press Enter twice, and the post is inserted twice.

Is there a solution to prevent this other than check if there is already a post with the same title and content?

Russell
  • 17,481
  • 23
  • 81
  • 125
user198729
  • 61,774
  • 108
  • 250
  • 348

15 Answers15

54

There are several solutions to this problem:

  1. Use Javascript to disable the form's submit button when it is posted. Downside to this is that this is ABSOLUTELY not a foolproof way. It's very easy to submit forms without actually clicking the button, and this would also not work for users with JavaScript disabled. I would definitely not recommend this method.

    Example:

    <script language="javascript">
    <!--
        function disableSubmitButton() {
            // you may fill in the blanks :)
        }
    -->
    </script>
    <form action="foo.php" method="post">
        <input type="text" name="bar" />
        <input type="submit" value="Save" onclick="disableSubmitButton();">
    </form>
    
  2. Use PHP sessions to set a session variable (for example $_SESSION['posttimer']) to the current timestamp on post. Before actually processing the form in PHP, check if the $_SESSION['posttimer'] variable exists and check for a certain timestamp difference (IE: 2 seconds). This way, you can easily filter out double submits.

    Example:

    // form.html
    <form action="foo.php" method="post">
        <input type="text" name="bar" />
        <input type="submit" value="Save">
    </form>
    
    // foo.php
    if (isset($_POST) && !empty($_POST)) 
    {
        if (isset($_SESSION['posttimer']))
        {
            if ( (time() - $_SESSION['posttimer']) <= 2)
            {
                // less then 2 seconds since last post
            }
            else
            {
                // more than 2 seconds since last post
            }
        }
        $_SESSION['posttimer'] = time();
    }
    
  3. Include a unique token on each POST. In this case, you would also set a session variable to the token you want to include and then render the token in the form. Once the form is submitted, you re-generate the token. When the submitted token does not match the token in your session, the form has been re-submitted and should be declared invalid.

    Example:

    // form.php
    <?php
        // obviously this can be anything you want, as long as it is unique
        $_SESSION['token'] = md5(session_id() . time());
    ?>
    <form action="foo.php" method="post">
        <input type="hidden" name="token" value="<?php echo $_SESSION['token'] ?>" />
        <input type="text" name="bar" />
        <input type="submit" value="Save" />
    </form>
    
    // foo.php
    if (isset($_SESSION['token']))
    {
        if (isset($_POST['token']))
        {
            if ($_POST['token'] != $_SESSION['token'])
            {
                // double submit
            }
        }
    }
    
Aron Rotteveel
  • 81,193
  • 17
  • 104
  • 128
  • 3
    +1 Great answer, but I'd edit so your recommendation comes first, and then you follow with the methods you don't recommend - would make for easier reading. That said, it should be the accepted answer. – Peter Bagnall Feb 13 '14 at 15:04
  • 2
    For the javascript solution, you could create that function for form submit event, not submit button click. That function would be called and would disable the submit button. – machineaddict Sep 16 '14 at 13:50
  • 2
    It seen that method Three is perfect – yip Jun 04 '15 at 08:23
  • 1
    I am not sure if this is correct. if you click submit twice on the same form, the same token will be submitted twice. hence, the token in the session needs to be reset once it is used. – Himanshu Ranavat Oct 15 '17 at 20:38
  • also confused as to why submitting the form twice will generate a different token the second time around. Can someone elaborate? – Robert Sinclair Jul 30 '18 at 15:48
  • 1
    If anyone interested to know why the token changes when we click submit a second time pls refer to this question https://stackoverflow.com/questions/51599485/why-is-a-new-token-generated-when-we-submit-form – Robert Sinclair Jul 30 '18 at 18:57
31

Use a unique token with the post, so that each post/postback is only handled once.

Disabling the submit button is not a very good solution since people could have javascript turned off, or doing other weird things. Client side validation is a good start, but should always be backed up with server side handling as well.

Mats Fredriksson
  • 19,783
  • 6
  • 37
  • 57
  • 2
    +1 - this is the most common method, and has no client-side dependencies. Why is this not accepted as the answer? – Russell Mar 11 '10 at 04:44
  • @Steve Not true. POSTs are not expected to be idempotent. – kaqqao Mar 10 '13 at 18:32
  • Refer to this question if you want to know why the token changes when we click "submit" a second time. https://stackoverflow.com/questions/51599485/why-is-a-new-token-generated-when-we-submit-form – Robert Sinclair Jul 30 '18 at 18:57
5

Generate a unique, one-time use key to submit with the form.

Relying on Javascript is a bad idea because it may have been disabled in the client by the user. With the key scheme, when the submit is received by the server, submission for that key can be locked. You can respond to duplicate submits however you like.

For this approach to work, keys must be unique and very difficult to predict. Otherwise some forms could be locked due to key collisions. So you don't have to track keys for every form submission and avoid collisions, keys should expire with that user's session. The other thing to watch out for is if malicious users are able to predict the key, your code may be vulnerable to some kind of hijacking or DOS exploit.

Dana the Sane
  • 14,762
  • 8
  • 58
  • 80
  • 1
    This is the approach I use. Generate a GUID whenever the page is rendered, and include this in your database whenever you save a form. In your submit processing code, make sure that GUID doesn't already exist in the db before inserting. – 3Dave Jan 25 '10 at 17:15
  • 1
    What type of GUID generation algorithm do you normally use? – Dana the Sane Jan 25 '10 at 17:19
2

Prevent Duplicate Form Submission shows a way of doing it without javascript.

ErikE
  • 48,881
  • 23
  • 151
  • 196
Javi
  • 19,387
  • 30
  • 102
  • 135
1

Disable the submit button using Javascript once the form gets submitted (onsubmit).

Note that if the user has disabled Javascript, they can still submit twice. Whether or not this happens often enough to justify also checking in your code (as you suggested in your question) is up to you.

Aistina
  • 12,435
  • 13
  • 69
  • 89
1

Alternatively, use one-time form keys.

mst
  • 247
  • 1
  • 5
1

I use this:

$form_token = $_SESSION['form_token'] = md5(uniqid());

send $form_token with the form, then...

I check the form_token:

if($_SESSION['form_token'] != $_POST['form_token']) {
   echo 'Error';
}
Johann
  • 33
  • 5
0

disable the submit button once the user has submitted the form, using JavaScript. That is what, for example, StackOverflow uses when you answer a question.

For example

<input type="submit" onclick="this.disabled=true" />
Marius
  • 57,995
  • 32
  • 132
  • 151
0

Use a JavaScript to disable the button as soon as it is clicked.

onclick="this.disabled=true;forms[0].submit();"
Asaph
  • 159,146
  • 25
  • 197
  • 199
Joel Etherton
  • 37,325
  • 10
  • 89
  • 104
  • 1
    The `onclick` handler won't run if the form is submitted via a keyboard stroke eg. user hits the enter key while focused on the submit button. Better use `onsubmit` like one of the other posters suggested. – Asaph Jan 25 '10 at 17:00
  • 1
    @Asaph: I actually like the token idea @Mats suggested. But if were staying client side, it would actually probably be better to have a method that incorporates both click and submit. 2 mouse clicks can register in IE before the onsubmit actually fires. Stupid, but I've seen it. – Joel Etherton Jan 25 '10 at 17:05
0

Pressing submit twice quite rarely results in multiple inserts, but if you want a dead-simple sollution, use

onsubmit="document.getElementById('submit').disabled = true"

in the form tag, provided you give your submit button id="submit"

raveren
  • 17,799
  • 12
  • 70
  • 83
0

Another way to create a confirmation page where user can finally press the submit button. It might reduce the possibilities of this kind.

0

I'm doing the following on the action.php

if($_SESSION && isset($_SESSION['already_submitted'])) {

  # If already submitted just redirect to thank you page

} else {

    # If first submit then assign session value and process the form
    $_SESSION['already_submitted'] = true;

    # Process form

}

Note: Always initiate your sessions in the header using session_start();

Robert Sinclair
  • 4,550
  • 2
  • 44
  • 46
-1

$(document).ready(function() {
  $('#postHideBtn').show();
  $('#postHideBtn').click(function() {
    $('#postHideBtn').hide();
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<form action="foo.php" method="post">
  <input type="text" name="bar" />
  <input type="submit" value="Save" id="postHideBtn" />
</form>
biberman
  • 5,606
  • 4
  • 11
  • 35
  • 1
    Please share the context of your code in details too. As the question asked has no context, at least provide yours :) – Dinesh Suthar May 28 '21 at 07:52
  • 1
    Just spitting out some code is not enough! You should explain it too. See https://stackoverflow.com/help/how-to-answer – biberman May 28 '21 at 15:13
-1

Aron Rotteveel's Include a unique token on each POST worked for me. However, my form still submits when a user refreshes the page (F5). I managed to solve this by adding a reset:

            // reset session token
            $_SESSION['token'] = md5( $_POST['token'] . time() );

my final scripts goes like:

if (isset($_SESSION['token'])) {
    if (isset($_POST['token'])) {
        if ($_POST['token'] != $_SESSION['token']) {
            echo '<h3>Oops! You already submitted this request.</h3>';
        } else {
            // process form
            // reset session token
            $_SESSION['token'] = md5( $_POST['token'] . time() );
        }
    } else {
        echo 'post token not set';
        $_SESSION['token'] = md5( $somevariable . time() );
    }
}

oh! don't forget to add session_start(); before <!DOCTYPE>

I am a PHP newbie so please correct me if you find irregularities.

Pabile
  • 1
  • 1
  • this is useless code. Just do an HTTP redirect after form submit. – Your Common Sense Nov 20 '11 at 08:08
  • i wish to do that but my form works in a single page. i initially wish to use jquery unload() but i find this useful on occasions user accidentally pressed f5 (page reload). do you have a link in that redirect? i would love to reconsider – Pabile Nov 20 '11 at 08:14
  • your reason makes no sense. you can do an HTTP redirect on the single page as well. – Your Common Sense Nov 20 '11 at 08:20
  • see this my answer for exampe http://stackoverflow.com/questions/7214860/printing-a-php-error-inline-instead-of-erasing-the-entire-page/7215138#7215138 – Your Common Sense Nov 20 '11 at 10:11
-1

Use JavaScript to stop it from sending

Simon
  • 1