WordPress Stripping UTM Parameters? Every Cause and How to Fix It

You’ve tagged your ad URLs with UTM parameters. You’ve set up hidden fields in your forms. You’ve done everything right — but when leads arrive, the UTM fields are blank. The parameters are gone. Something between the ad click and the form submission stripped them out.

This is one of the most common and frustrating problems in WordPress marketing. The platform itself — through redirects, caching, and URL normalisation — can silently remove UTM parameters before your tracking code ever sees them. And if you’re running WooCommerce, the multi-step checkout flow introduces even more points where attribution data gets lost.

This guide documents every known cause of UTM parameter stripping in WordPress, with the specific fix for each one. Work through them in order — the most common causes are listed first.

Cause 1: Page Caching Serves Stale or Empty UTM Values

Likelihood: Very common

This is the single most common reason UTM parameters fail in WordPress. If your site uses any form of page caching — whether through a plugin (WP Rocket, W3 Total Cache, WP Super Cache, LiteSpeed Cache) or through your hosting provider (WP Engine, Kinsta, Cloudflare, SiteGround) — cached pages can break server-side UTM capture.

Here’s what happens: when the first visitor arrives at /contact/?utm_source=google&utm_medium=cpc, the server renders the page, including any PHP that reads query string values (like form hidden field population). The caching layer then saves this rendered HTML. When the second visitor arrives at /contact/?utm_source=facebook&utm_medium=social, the cache serves the saved HTML from the first visitor — complete with the first visitor’s UTM values baked into the hidden fields.

The result: incorrect attribution data, or no data at all if the cached version had empty values.

How to Diagnose

Test while logged in (where caching is usually disabled) and then in an incognito window (where caching applies). If UTM values appear in form entries when you’re logged in but not when you’re logged out, caching is the cause.

The Fix

Option A: Exclude the form page from cache. In your caching plugin’s settings, add the URL of your form page to the cache exclusion list. This forces the server to render the page fresh for every visitor. Downside: you lose the performance benefit of caching for that page.

Option B (Recommended): Use client-side JavaScript instead of server-side PHP. A JavaScript snippet that reads UTM parameters from the URL and stores them in cookies runs in the visitor’s browser — after the cached HTML is served. Caching doesn’t affect it because the JavaScript executes on the client, not the server. This is the approach we recommend and detail in our JavaScript UTM capture guide.

Cause 2: Managed Hosting Strips UTM Parameters at the Server Level

Likelihood: Common (WP Engine, some Cloudflare configurations)

Some managed WordPress hosts actively remove UTM parameters from the URL before the request even reaches PHP. WP Engine is the most well-known example — their servers “sanitise” incoming requests by stripping utm_ and gclid parameters from the URL to improve cacheability.

This means that even if you’ve excluded the page from cache, server-side PHP code like $_GET['utm_source'] will return nothing because the parameter was removed from the request before WordPress processed it.

How to Diagnose

Add a temporary test to your theme’s functions.php:

add_action('wp_head', function() {
    echo '';
});

Visit your site with ?utm_source=test in the URL. View the page source. If the debug comment shows “NOT FOUND” even though the URL contains the parameter, your host is stripping it.

The Fix

Option A: Ask your host to add cache exclusions for UTM parameters. On WP Engine, you can request this through their support portal. They can configure their Varnish cache to pass through URLs containing utm_ parameters without stripping them. Be aware that this may increase uncached traffic and affect site performance.

Option B (Recommended): Use client-side JavaScript. JavaScript reads the URL directly in the visitor’s browser, before the server has a chance to strip anything. The URL bar still shows ?utm_source=google even on WP Engine — it’s only the server-side request that gets sanitised. A client-side JavaScript snippet captures the values from window.location.search before the server ever processes the request.

Cause 3: WordPress Canonical Redirects Strip Query Parameters

Likelihood: Moderate

WordPress includes a function called redirect_canonical() that normalises URLs by adding trailing slashes, fixing capitalisation, and resolving old slugs. During this process, query parameters can be dropped.

The most common scenario: a visitor arrives at /contact?utm_source=google (no trailing slash). WordPress sees that the canonical URL is /contact/ (with trailing slash) and issues a 301 redirect. During this redirect, the query parameters may be stripped or re-processed, depending on your server configuration and WordPress version.

How to Diagnose

Open your browser’s Developer Tools (F12), go to the Network tab, and visit your page with UTM parameters but without the trailing slash (e.g., /contact?utm_source=test). Look for a 301 redirect. If the redirect target URL has lost the query parameters, this is the cause.

The Fix

Option A: Ensure your ad URLs include the trailing slash. Use yoursite.com/contact/?utm_source=google (note the / before the ?), not yoursite.com/contact?utm_source=google. This prevents the canonical redirect from firing at all.

Option B: Use the redirect_canonical filter to preserve parameters. Add this to your child theme’s functions.php:

add_filter('redirect_canonical', function($redirect_url, $requested_url) {
    // Preserve query string during canonical redirect
    $query = wp_parse_url($requested_url, PHP_URL_QUERY);
    if ($query && strpos($redirect_url, '?') === false) {
        $redirect_url .= '?' . $query;
    }
    return $redirect_url;
}, 10, 2);

Option C (Recommended): Use client-side JavaScript. JavaScript captures the UTM parameters from the original URL before any redirect occurs. Even if the redirect strips the parameters from the final URL, the JavaScript has already read and stored them in cookies.

Cause 4: Pretty Permalinks and Reserved Query Variable Names

Likelihood: Moderate (often misdiagnosed)

There’s a common misconception that WordPress’s pretty permalink structure breaks UTM parameter capture. It doesn’t — at least not for UTM parameters specifically. Here’s what actually happens:

WordPress uses an internal URL rewriting system (WP_Rewrite) to translate pretty URLs like /contact/ into internal query strings like ?pagename=contact. When you read query parameters with $_GET in PHP, standard UTM parameters (utm_source, utm_medium, etc.) work fine because they don’t conflict with WordPress’s internal query variables.

The problem occurs when you use reserved WordPress query variable names as parameter names. WordPress reserves certain names for internal use, and using them triggers unexpected behaviour:

Reserved NameWhat WordPress Uses It ForWhat Happens If You Use It
namePost slug lookup404 error
pagePagination301 redirect, value stripped
pPost ID lookupRedirects to a different post
page_idPage ID lookupRedirects to a different page
catCategory IDLoads category archive instead
tagTag slugLoads tag archive instead
year, month, dayDate-based archivesLoads date archives instead

Standard UTM parameters (utm_source, utm_medium, utm_campaign, utm_term, utm_content) are not reserved and work fine with pretty permalinks. Click ID parameters (gclid, fbclid, msclkid, li_fat_id, ttclid) are also not reserved.

The Fix

If you’re using custom parameters (beyond standard UTMs), check them against the WordPress reserved terms list. If any conflict, rename them with a prefix (e.g., lsp_source instead of source).

If you need WordPress to recognise your custom query variables for WP_Query (not typically needed for UTM tracking), register them via the query_vars filter:

add_filter('query_vars', function($vars) {
    $vars[] = 'my_custom_param';
    return $vars;
});

But for UTM tracking, you typically don’t need this — you’re reading the values with JavaScript or $_GET in PHP, not using them in WP_Query.

Cause 5: Security Plugins Blocking Query Parameters

Likelihood: Uncommon but hard to diagnose

Security plugins like Wordfence, Sucuri, and iThemes Security (Solid Security) can block or flag URLs with unusual query parameters. While standard UTM parameters are rarely affected, click IDs like gclid and fbclid — which contain long, seemingly random strings — can trigger firewall rules that interpret them as SQL injection or cross-site scripting attempts.

How to Diagnose

Temporarily deactivate your security plugin and test with UTM parameters and click IDs. If the parameters start working, the security plugin is the cause. Check the plugin’s firewall logs for blocked requests containing your query parameters.

The Fix

Add your form page URLs to the security plugin’s whitelist or allowlist. In Wordfence, this is under Firewall > Whitelisted URLs. In Sucuri, check the Firewall settings for blocked parameters. Don’t disable the firewall entirely — just whitelist the specific URLs that receive ad traffic.

Cause 6: SEO Plugins Stripping Parameters from Canonical Tags

Likelihood: Indirect (affects SEO, not form tracking directly)

SEO plugins like Yoast SEO, Rank Math, and All in One SEO add <link rel="canonical"> tags to your pages. By design, these canonical tags strip query parameters from the URL to prevent duplicate content issues. For example, Yoast outputs <link rel="canonical" href="https://yoursite.com/contact/" /> even when the actual URL is /contact/?utm_source=google.

This doesn’t directly affect your form tracking (the canonical tag is just a hint to search engines, not a redirect). However, it can cause confusion when debugging because the canonical URL in the page source won’t show UTM parameters — which can lead you to think they’ve been stripped when they haven’t.

The Fix

No fix needed for form tracking — this is expected and correct SEO behaviour. The canonical tag tells search engines to index the parameter-free URL, which is what you want. Your JavaScript UTM capture script reads from window.location.search, not from the canonical tag, so it’s unaffected.

WooCommerce: Why Checkout Loses UTM Parameters

WooCommerce introduces additional points of UTM parameter loss beyond the standard WordPress issues. The e-commerce checkout flow passes through multiple pages, and UTM data can disappear at any step.

The Multi-Step Checkout Flow

A typical WooCommerce purchase involves five or more page transitions:

  1. Landing page (with UTM parameters in the URL)
  2. Product page (parameters gone from URL)
  3. Cart page (parameters gone)
  4. Checkout page (parameters gone)
  5. Payment gateway redirect (leaves your site entirely)
  6. Thank-you/order confirmation page (parameters gone)

By step 2, the UTM parameters have already vanished from the URL. Any server-side code that reads $_GET['utm_source'] on the checkout page will find nothing.

WooCommerce Order Attribution (Built-in)

WooCommerce 8.5+ includes a built-in Order Attribution feature that uses session cookies (via Sourcebuster.js) to track the source of each order. This is a step in the right direction, but it has limitations:

  • Session cookies expire when the browser closes. If a customer clicks your ad today but returns tomorrow to complete the purchase, the attribution data is gone.
  • Safari ITP limits cookies to 7 days. On Safari (roughly 27% of mobile traffic), even persistent cookies set by JavaScript expire after 7 days.
  • No click ID capture. The built-in system doesn’t capture gclid, fbclid, or other ad platform click IDs, which are essential for offline conversion tracking.

Payment Gateway Redirects

When a customer clicks “Place Order,” they’re often redirected to a third-party payment gateway (Stripe, PayPal, a bank’s 3D Secure page). When they return to your site after payment, they arrive at the thank-you page with a completely different URL. Any server-side tracking that relied on the URL is lost. Only cookie-based tracking survives this round trip.

The Fix for WooCommerce

Use persistent cookies with a 90-day expiry. The JavaScript UTM capture script stores attribution data in the visitor’s browser on the first page load. The cookies persist through the entire checkout flow — product browsing, cart, checkout, payment gateway redirect, and back. When the order is placed, a server-side hook reads the cookies and attaches the attribution data to the WooCommerce order.

For WooCommerce integration, you can use the woocommerce_checkout_order_processed action hook to read cookies and save them as order meta:

add_action('woocommerce_checkout_order_processed', function($order_id) {
    $params = ['utm_source', 'utm_medium', 'utm_campaign',
               'utm_term', 'utm_content', 'gclid', 'fbclid',
               'msclkid', 'landing_page', 'referrer_url'];

    foreach ($params as $param) {
        if (isset($_COOKIE[$param])) {
            update_post_meta($order_id, '_' . $param,
                sanitize_text_field($_COOKIE[$param]));
        }
    }
});

This saves the attribution data as order meta, which you can then view in the WooCommerce order details screen or export to your CRM.

Quick Diagnostic Checklist

If your UTM parameters aren’t working, run through this checklist in order:

CheckHow to TestIf Failing, See
UTMs work when logged in but not logged out?Test in incognito windowCause 1: Caching
UTMs work locally but not on your live host?Add PHP debug output to check $_GETCause 2: Host stripping
URL redirects (301) before the page loads?Check Network tab in DevTools for 301Cause 3: Canonical redirect
Using reserved parameter names (page, name, p)?Check your parameter names against reserved listCause 4: Reserved names
Security plugin firewall blocking requests?Temporarily deactivate security pluginCause 5: Security plugin
WooCommerce orders show no source data?Check if cookies persist through checkout flowWooCommerce section

The Fix That Bypasses All of This: LeadSourcePro

Every cause listed above is a server-side problem. The server strips the parameters, the server caches stale values, the server redirects and drops query strings. The common thread in every fix is the same recommendation: use client-side JavaScript instead of server-side PHP.

LeadSourcePro takes this approach to its logical conclusion. It captures UTM parameters, click IDs (gclid, fbclid, msclkid, li_fat_id, ttclid), the referrer URL, and the landing page entirely on the client side — in the visitor’s browser — and stores them using first-party cookies that persist for 90 days. Server-side caching, host-level sanitisation, canonical redirects, and pretty permalinks have no effect because the capture happens before any server processing.

LeadSourcePro then attaches the attribution data to form submissions (across nine form plugins including Contact Form 7, Gravity Forms, and WPForms) and WooCommerce orders automatically — no hidden fields, no PHP hooks, no configuration.

Frequently Asked Questions

Do UTM parameters affect WordPress SEO?

No. SEO plugins add canonical tags that point to the parameter-free URL, so search engines see /contact/ rather than /contact/?utm_source=google. Google treats UTM parameters as tracking decorations and ignores them for indexing purposes. Your pages won’t be penalised for duplicate content.

Should I disable page caching to fix UTM tracking?

No — disabling caching entirely will hurt your site’s performance. Instead, either exclude specific form pages from cache (if you’re using server-side UTM capture) or switch to client-side JavaScript capture, which works regardless of caching. The JavaScript approach is the better solution because you keep full caching performance while still capturing UTM data accurately.

Why does WP Engine strip UTM parameters?

WP Engine strips utm_ and gclid parameters at the server level to improve cache efficiency. If every unique combination of UTM parameters created a separate cached page, the cache would grow rapidly and reduce hit rates. Their solution is to sanitise the URL before caching so all visitors get the same cached page. WP Engine recommends using JavaScript for UTM capture instead of server-side PHP. You can also request cache exclusions through their support, but this may impact performance.

Do pretty permalinks break UTM parameters?

No — this is a common misconception. Pretty permalinks rewrite the URL structure for SEO purposes but don’t strip query string parameters. The standard UTM parameters (utm_source, utm_medium, utm_campaign, utm_term, utm_content) and click IDs (gclid, fbclid, etc.) work fine with pretty permalinks. What often looks like a permalink problem is actually a canonical redirect triggered by a missing trailing slash — see Cause 3 above.

How do I preserve UTM parameters in WooCommerce orders?

Store UTM values in persistent cookies (90-day expiry) using client-side JavaScript. Then use the woocommerce_checkout_order_processed hook to read the cookies and save them as order meta. This approach survives the entire checkout flow, including payment gateway redirects. Alternatively, LeadSourcePro handles this automatically with no code required.

Stop fighting WordPress to keep your UTM data

LeadSourcePro captures UTM parameters, click IDs, referrer URL, and landing page automatically — bypassing every WordPress stripping issue listed above. No caching conflicts, no redirect problems, no code to maintain. Install LeadSourcePro and see where your leads are really coming from.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *