How to Prevent Duplicate Form Submissions (Without Breaking Your Tracking)

If you’re tracking form submissions using a thank you page, you’ve probably run into this problem:

👉 Users refresh the page and your conversion fires again.

This leads to inflated numbers in GA4, Meta Ads, or any other platform you’re using.

There are two main ways to fix this:

  1. Using an ID in the URL
  2. Controlling duplication directly in the browser (recommended)

Let’s break both down.


Option 1: Add an ID to the Thank You Page URL

The idea here is simple: every time a form is submitted, you append a unique identifier to the URL.

Example:

https://sanrivalina.com/thank-you-page/?entry_id=12345

How to implement it (WPForms)

WPForms does NOT reliably expose entry_id as a Smart Tag in the redirect URL. So if you want the real entry ID, you’ll need a small PHP filter:

add_filter( 'wpforms_process_redirect_url', function( $url, $form_id, $fields, $form_data, $entry_id ) {
    return $url . '?entry_id=' . $entry_id;
}, 10, 5 );

If you don’t want to use code, you can pass field values instead:

https://sanrivalina.com/thank-you-page/?email={field_id="1"}

Pros

  • You get a unique identifier per submission
  • Useful for debugging or backend matching

Cons

  • Requires PHP for real entry IDs
  • The same URL can still be refreshed (duplicates still possible)
  • Users can share the URL
  • Not ideal for clean tracking setups

👉 Important: This does NOT solve duplication by itself.


Option 2: Prevent Duplicates with sessionStorage (Recommended)

Instead of relying on the URL, you control duplication directly in the browser.

The idea:

👉 Once the conversion fires, you store a flag in the session.
👉 If the page reloads, the flag blocks the trigger.


Step 1: Create a Variable in GTM

Type: Custom JavaScript

function() {
  return sessionStorage.getItem('lead_form_contact');
}

Step 2: Create Your Trigger

Name:

TR - Pageview - Thank You Page

Conditions:

  • Page URL contains thank-you-page
  • AND
  • JS - Session - Lead Contact does not equal true

This is the key piece. This condition is what blocks duplicates.


Step 3: GA4 Event Tag

Name:

GA4 - Event - Lead Contact

Trigger:

TR - Pageview - Thank You Page


Step 4: Store the Flag (Custom HTML Tag)

Name:

UTIL - Session Flag - Lead Contact

<script>
  sessionStorage.setItem('lead_form_contact', 'true');
</script>

Trigger:

👉 Use the SAME trigger as the GA4 event


What’s actually happening?

First visit

  • No flag exists
  • Trigger conditions pass
  • GA4 event fires
  • Flag is stored

Page reload

  • Flag already exists
  • Trigger condition fails
  • Nothing fires

That’s it. No duplicates.


Which Option Should You Use?

If your goal is clean, reliable tracking:

👉 Go with sessionStorage.

If your goal is backend validation or debugging:

👉 You can add an ID to the URL (but don’t rely on it for tracking logic).


Final Recommendation

Use this setup:

  • Thank you page trigger
  • sessionStorage flag
  • Clean URL (no parameters required)

It’s simple, scalable, and aligns with how tracking is actually handled in production environments.


If you want to take it one step further, the best setup is to fire the conversion on form submission (not page load), but that’s a different level of implementation.

For most cases, this approach will keep your data clean and reliable.

Similar Posts