How to prevent multiple form submission on multiple clicks in PHP

Use a unique token generated each time you display a form and which can be used only one time; it is also usefull to prevent CSRF and replay attacks. A little example :

<?php
session_start();

/**
 * Creates a token usable in a form
 * @return string
 */
function getToken(){
  $token = sha1(mt_rand());
  if(!isset($_SESSION['tokens'])){
    $_SESSION['tokens'] = array($token => 1);
  }
  else{
    $_SESSION['tokens'][$token] = 1;
  }
  return $token;
}

/**
 * Check if a token is valid. Removes it from the valid tokens list
 * @param string $token The token
 * @return bool
 */
function isTokenValid($token){
  if(!empty($_SESSION['tokens'][$token])){
    unset($_SESSION['tokens'][$token]);
    return true;
  }
  return false;
}

// Check if a form has been sent
$postedToken = filter_input(INPUT_POST, 'token');
if(!empty($postedToken)){
  if(isTokenValid($postedToken)){
    // Process form
  }
  else{
    // Do something about the error
  }
}

// Get a token for the form we're displaying
$token = getToken();
?>
<form method="post">
  <fieldset>
    <input type="hidden" name="token" value="<?php echo $token;?>"/>
    <!-- Add form content -->
  </fieldset>
</form>

Combine it with a redirect so you keep a perfect backward and forward behavior. See the POST / redirect / GET pattern for more information about the redirect.


You could disable the button after the first click (using JavaScript) and also have a check on the back-end (just in-case they disabled their JavaScript) which checks if they just recently submitted.

There are quite a few different ways of doing the check on the back-end. One way would be to set a session variable when they click it the first time, which can let the system know that it's processing. If they click a second, third or fourth time, then it can just check the session variable, and if that indicates that it's already been clicked, it won't process.

That's just one example - you could use that as a start.


I recommend against disabling the submit button, because in case of a temporary network issue (i.e. the request has not gone through at all), if the user chooses to abort the submission (Esc key/Stop button), he cannot submit again once network service has been restored, and instead will have to reload the page and fill in all the form entries again.


You could add something like this

<input type="hidden" name="form-token" value="someRandomNumber">

Then on the back-end, you have a save way of identifying multiple form submissions. This solution, of course, has some baggage since you have to delete form-tokens you know have finished processing, etc...

In most cases, a disabled form does the trick, for instance by either disabling the button or by adding return false to the form submit event (not sure if this works without jQuery though).