Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

AliMalik's avatar

Stopping Multiple Form Submission.

How can i check for if a form is already submitted (posted) and then disabling further submission requests on server side in laravel

0 likes
30 replies
davidfaux's avatar

You need to disable the submit button on click, an e.g. using jQuery

<button id="register" type="submit">Register</button>
$('#register').click(function() {
        $(this).attr('disabled','disabled');
});
3 likes
AliMalik's avatar

@dfaux Thank you , but this is client side solution i.e disabling the submit button , but i still have problems with it like upon clicking submit button if form contains invalid data and validation jumps in to prompt , then submit button is already disabled. No chance for user to use it again after correcting input.

davidfaux's avatar

If you're doing your validation server side the page is reloading and the submit button will be enabled again. If you're doing your validation client side add this click event post validation

1 like
mstnorris's avatar

@AliMalik you could set a token when you serve the form and check that against the database. When you submit the form, the token is checked and you can't submit it any more. Of course, it is still a good idea to do it front-end too as it is more visual for the user.

1 like
pmall's avatar

Do it frontend. Just re-enable the button if validation fails. It is much more complicated to do it on the backend for a few advantages...

1 like
mstnorris's avatar

@pmall I agree with you however we had a conversation yesterday where it went down the lines of "what if they have JavaScript turned off" and "some State agencies don't allow JavaScript". Personally, I think most people will and chances are the users of your app will. But there are always some who like to cater for every eventuality.

@AliMalik personally, if you can just do it with JavaScript then I would go down that route.

1 like
pmall's avatar

@mstnorris I don't think the pain of doing it server-side is worth the few guys without javascript who will click multiple times on the button.

1 like
mstnorris's avatar

@pmall I totally agree with you, I wouldn't do it, but just mentioning to the OP that it is an option should the requirement be to absolutely not rely on the front-end.

1 like
AliMalik's avatar

Thank you guys @pmall @mstnorris . I have done it on front-end. i just disable the submit button and re-enable it if validation fails.

AliMalik's avatar

But this idea of "What if user have their JavaScript disabled " and stuff like that , is not it a safe idea to always implement server side logic and checks for web apps, after all there are chances that "Users will do something silly" with our app.

timavo's avatar

Due to the fact that the csrf token is stored for a whole session and therefore doesn't change after each form submission, resubmitting forms is possible.

To permit resubmitting forms, I added the tokensMatch() function in app/Http/Middleware/VerfiyCsrfToken.php (which will overwrite the inherited one):

protected function tokensMatch($request)
{
    $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

    if (!$token && $header = $request->header('X-XSRF-TOKEN')) {
        $token = $this->encrypter->decrypt($header);
    }

    $tokensMatch = Str::equals($request->session()->token(), $token);

    if($tokensMatch) $request->session()->regenerateToken();

    return $tokensMatch;
}

This will force laravel to regenerate a new session token after each time a token is verified correctly.

In case you validate the form and the validation fails, the old data will be passed back to the form. So you need to make sure not to pass back the old token by adding "_token" to the $dontFlash array in app/Http/Requests/Requests.php.

protected $dontFlash = ['password', 'password_confirmation', '_token'];

As I am new to laravel I can't guarantee that it won't conflict with any other laravel functionality. So it would be cool if someone more experienced can review this option.

Thanks @dberry for the tip not to edit the vendor files.

5 likes
dberry's avatar

@timavo, definitely should not edit the vendor file.

just add your code to app/Http/Middleware/VerifyCsrfToken.php this extends the base class and is the file that gets included in the app/Http/Kernel.php.

Same with the FormRequest... Look inside app/Http/Requests.php

1 like
tang3's avatar

@timavo That does not work in laravel 5.2. I had regenerateToken, but I found the token was always the same. I have no idea what is wrong with that.

brandonf's avatar

Also looking for a server side solution, front end alone will not work for my project.

Snapey's avatar

@brandonf as mentioned earlier, create a unique token for your form and save it in the session. Pass it to the browser in a hidden form field.

When the form is posted and validation passes, find and clear out the matching token from session. If you found it then process the form. If you can't find one then the current request is a duplicate.

Earlier solutions suggested hijacking the csrf token but this seems to overly complicate things.

1 like
leyduana's avatar

the jquery thing is usedless. when clicked it does not submit a form.

peterlc's avatar

This is how i dit.

In create.blade:


    $(document).ready(function () {

        $('#update').click(function () {
            $('#update').attr('disabled', true);
            $('#createForm').submit();
            return true;
        });

    });
</script>

and in edit.blade:


    $(document).ready(function () {

        $('#update').click(function () {
            $('#update').attr('disabled', true);
            $('#updateForm').append('<input type="hidden" name="update" value="1" />').submit();
            return true;
        });

 });
shayne.oneill's avatar

Telling people not to validate server side is terrible advice. The golden rule of web security is NEVER trust the client! (Well that and avoid interpolation like the plague)

What you SHOULD be doing is validate at the front end AND validate at the server. If you really must chose , then chose to do it at the server.

The most straight forward way to guarantee the uniqueness of a form submission (In the sense of stopping someone mashing submit twice) is to generate a random token and storing it in a session AND a hidden field. If it doesnt match, reject the form, if it does match, accept the form and nuke the session key. While this is not completely ideal in a 12factor app, you can always store the key in a database table and mark it expired once its successfully submitted.

2 likes
georaldc's avatar

@shayne.oneill what happens from a user's standpoint if a duplicate form submission is encountered and rejected? I'm guessing they get whatever response comes from the latest request (the duplicate one), whether it be an exception, a redirect, etc?

For some reason, even with client-based solutions like disabling submit buttons, I still encounter random double submissions going through (I know because I get logged database errors like attempted insertions on rows with unique constraints). I wonder if I'm missing something else

Zanduk's avatar

New to Laravel, but had the same issue about duplicate form posts. My solution was to check (in the controller) if the form post exists for the current user. If a post had already been submitted, I redirected to another page; otherwise I returned the form view.

agilasadi's avatar

I'm dealing with the same issue. But the real issue is that, this can happen even with the get requests, you need to somehow disable user from sending same request multiple times in a short duration. you eather should get requests delied in orther to see if the user is going to send it multiple times or you should disable request button for a few seconds once used. but this solution wont help much

ahmed-aliraqi's avatar

@timavo in your solution there are issue if you open your project in more browser tabs the token in the other tabs will be invalid.

The only solution. you should disable submit button when click it.

<input type="submit" onClick="this.disabled=true; this.value='Sending…';">
dfsdfsdfsdf's avatar

The following code takes every form that doesn't have the no-block class, waits for it to be submitted and blocks the submit buttons in those forms. This works best with html5 validation and no extra js. To be 100% sure, i'm adding a transparent overlay, so that no other buttons on the page can be clicked.

var forms = document.querySelectorAll("form:not(.no-block)");
    forms.forEach(function(form) {
        form.addEventListener('submit', formSubmitted);
    });
}

function formSubmitted(e) {
    var submitButtons = e.target.querySelectorAll("button[type=submit],input[type=submit]");
    submitButtons.forEach(function(submitButton) {
        if (submitButton.tagName === "INPUT") {
            submitButton.value = "Please wait...";
        } else {
            submitButton.innerHTML = '<i class="fa fa-circle-o-notch fa-spin fa-fw"></i>';
        }
        submitButton.disabled = true;
    });

    var overlay = document.createElement("div");
    overlay.className = "submit-overlay";
    document.body.appendChild(overlay);
}

And here's the css:

.submit-overlay {
    position: fixed;
    z-index: 10000;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background: rgba(255, 255, 255, 0);
}
1 like
NielsNumbers's avatar

generate a random token and storing it in a session AND a hidden field. If it doesnt match, reject the form, if >it does match, accept the form

How is this save against raise conditions? If someone double clicks the submit button, then it may happen that the script compare session and hidden field for both requests simultaneously - so both request pass through - right?

benfurfie's avatar

As @shayne.oneill said, you should absolutely never rely solely on client side validation.

One reason that hasn't been mentioned here is if someone makes a direct POST request to the form endpoint, and they have captured the CRSF token to pass along in the header, then all your JS-based solutions count for nothing because they never touch your clientside.

It's crucial to implement some form of rate limiting on the server side in this instance. You can use the cache or a database to achieve this, but relying on client side validation to protect you against this kind of malicious attack is pointless.

tanvirprince's avatar

Step 1: write a class name in the form tag Exp: "from-prevent-multiple-submits"

@csrf

Step 2: Write a class in button section class="Exp: "from-prevent-multiple-submits"

Step 3: write this script code

<script type="text/javascript">
(function(){
$('.from-prevent-multiple-submits').on('submit', function(){
    $('.from-prevent-multiple-submits').attr('disabled','true');
})
})();
</script>
Jacotheron's avatar

I solved this issue a while back, granted my form to be submitted was at the end of a wizard-style process (in which all steps had to be performed, each storing its result in session data, and this form finally processing all of that data - taking a while to complete [the original code was written by a different developer]).

I solved it by first checking if a session key was set (for example 'submitting-form'), if it was set, exit early (here I can return a json result which would simply be ignored, as the form was submitted by ajax), and if not to set it and continue. Since 2 (or perhaps more) requests from the same user should use the same session data, the narrow opportunity to get a 2nd request through (as both should read that it was not set, and then set it) was small enough to prevent 99.9% of the 2nd form requests. When the process was done (also clearing all other data that was used during the process), it would return a json which would redirect them to a different page, and this page would unset the session key (and thus allow them to potentially go through the whole process again, should they want to).

quanle199's avatar

I think you can use a "a" tag for a link with onclick then css to it look like button and put a real button at the end

Please or to participate in this conversation.