Conversion

Why do I get duplicate form submissions?

By Jake Luo · Published Sep 22, 2026

Because the form really was submitted more than once, and nothing on the receiving end noticed. A page sends the same enquiry twice for at least five ordinary reasons — a second click, a retry of a request that had already succeeded, a refresh, two handlers wired to one form, and bots — so disabling the button after the first press fixes one of them and leaves the rest. The durable fix sits on the receiving end: ignore an identical submission from the same form inside a short window, and answer that repeat exactly as if it had been accepted.

Five ways one enquiry becomes three

The instinct is to suspect the form, and the form is usually fine. Duplicates are what you get when an ordinary page meets an ordinary network, and the useful first move is not a fix at all — it is working out which of these you are looking at, because they do not have the same answer.

  1. The visitor pressed submit twice. Nothing visibly changed after the first press, so they pressed again. This is the commonest cause, and the only one that the usual advice of disabling the button actually addresses.
  2. A request was retried after it had already worked. The submission reached your handler, the handler stored it, and the response was lost on the way back. As far as the sender is concerned it failed, so it tries again. This is the one that produces identical copies a few seconds apart, and no amount of care in the page prevents it.
  3. The page was refreshed, or reached again with the back button. A form that posts and then renders its own thank-you in place will happily offer to send the whole thing again when somebody reloads. These copies arrive minutes or hours apart, which is the tell.
  4. Two things are handling one form. A host's built-in form handler plus a script that a generator or a plugin added, each sending its own copy. They arrive simultaneously and are often shaped differently — one carries every field, the other turns up half empty.
  5. It was not a person. Link prefetchers, security scanners and ordinary bots reach form endpoints and sometimes submit them. These copies are unhelpful rather than merely repeated: blank fields, nonsense values, or the same payload from many addresses. A honeypot field — one input a human never sees and never fills in — removes most of them for almost no effort.

Two readings sort most cases out. Look at the gap between the copies: simultaneous points at two handlers or a bot, a few seconds at a second click or a retry, minutes or more at a reload. Then look at whether the copies are identical, because a genuine second enquiry from a real person almost always differs somewhere, even by a word.

The fix belongs on the receiving end

Everything on that list except the bots is normal behaviour, not a defect you can remove. Networks drop responses, people click twice, browsers re-send what they were told to send. None of that is going to stop, so the thing that has to change is what happens when the same enquiry arrives for the second time.

The mechanism is small. Derive a short fingerprint from the content of the submission, remember it for a few minutes, and if identical content arrives again inside that window, do not store a second copy. That covers the second click, the network retry and the reload at once, because it does not care which of them happened — only that this exact enquiry has been seen already.

The part that is easy to get wrong is what you answer the duplicate with. We host the pages our customers publish, so their contact forms all post to one endpoint of ours, and duplicates were a complaint that kept coming back: three separate customer sites, with one enquiry stored three times, five times, and twice. It recurred until the endpoint began answering the duplicate byte for byte as though it had been accepted. An endpoint that refuses a repeat, even politely and with a helpful message, tells the page in front of it that something went wrong, and then the page's own retry logic or the visitor reading the error sends it once more. A deduplicating endpoint that looks like a failure is a duplicate generator.

Choosing the window, and what not to throw away

The window length is the only real judgement call, and it trades one mistake against another. Too short and a slow retry still lands. Too long and you silently discard a real second enquiry — the person who wrote in the morning, heard nothing, and wrote again in the same words. A few minutes covers the mechanical causes, which all happen fast; we settled on ten, and a contact form is forgiving here because a genuine second enquiry that close together is rare.

Fingerprint the fields the visitor filled in, and nothing else. It is tempting to hash the whole request, and that quietly disables the entire thing, because a timestamp, a session token or a tracking parameter differs on every send — so every duplicate looks unique and nothing is ever caught. The same caution applies to anything your own page attaches to the submission after the visitor has finished with it.

Last, count the duplicates you drop instead of discarding them in silence. A deduplicating endpoint and a broken delivery path look identical from an empty inbox, and one of the two is far more urgent: enquiries that never arrive at all is a different investigation, and you want to be able to rule this one out quickly rather than wondering whether your own fix ate the message.

FAQ

Will disabling the submit button after the first click fix this?
It fixes one cause out of five, and it is still worth doing, because that cause is the commonest and the change is trivial. Give the visitor immediate feedback that the press registered, and disable the control while the request is in flight. What it cannot touch is a retry that happens below the page, a reload, a second handler, or a bot — so treat it as the polish and the receiving-end check as the fix.
Should I use a unique token per form instead of hashing the content?
If you control both ends, yes: that is the stronger version, and it is what payment APIs do. The sender generates one key when the form is rendered and reuses it on every retry, so the receiver recognises a retry exactly rather than inferring it. The reason to hash content instead is that it needs nothing from the sender, which matters when the form was produced by a site builder, a generator or a plugin you do not control.
How should I clean up the duplicates I already have?
Group by content rather than by timestamp, keep the earliest of each group, and read a sample before deleting anything, because two genuinely separate enquiries can be word-for-word identical when somebody follows up after silence. If the copies landed in your inbox as email, the same grouping works there, and it is worth checking whether your reply went to only one of them. Fix the endpoint first, or you will be doing this again next week.
Related questions
Why am I not getting enquiries from my website?Does my website need a backend?How much of my website traffic is actually bots?How do I turn website visitors into signups?

Want this done for you?

AgentCeres is a managed AI marketing team — specialists draft the work, you approve what ships. 14-day free trial, from $39/month.

Start free trialMore answers