Technical Brief

FIX GCLID STRIPPING
ON REDIRECT

Google Click ID (GCLID) is the lifeline of ad attribution. When it breaks, revenue tracking vanishes. This brief explores the mechanics of parameter loss, diagnosis, and server-side remediation.

01. The GCLID Lifecycle

The Google Click ID is a unique alphanumeric string appended to your landing page URL. It bridges the gap between the Ad Click and the Conversion Event.

Technical Role

Used for auto-tagging. Decoded by GA4 and Google Ads to attribute Campaign, Ad Group, and Keyword data.

FLOW_DIAGRAM_V1
1

Ad Click

google.com/search

2

Landing Page

yoursite.com/?gclid=...

3

Conversion

Matches GCLID

02. The Mechanics of Data Loss

GCLID stripping occurs when a request is intercepted before reaching the final destination, and the query parameters are not explicitly passed to the new URL. This is most common in 301/302 redirects.

  • !

    Server Redirects: Apache/Nginx rewrite rules that don't include $query_string or QSA flags.

  • !

    CMS Plugins: WordPress security or caching plugins (e.g., "Remove Query Strings from Static Resources").

  • !

    Protocol Mismatch: Redirecting from http:// to https:// often drops params if not configured correctly.

Estimated breakdown of GCLID stripping incidents by root cause.

03. Engineering Diagnostic Workflow

1. URL Inspection

01

Manually append a fake test parameter to your URL to see if it survives the load.

?gclid=TeSt_123

If the URL loads but the parameter is gone from the address bar, stripping is active.

2. Network Tab

02

Open Chrome DevTools > Network. Enable "Preserve Log". Click your ad link.

Status: 301 Moved Permanently Location: /new-path (No Params)

Look for 301/302 status codes followed by a request to a URL without the gclid.

3. Curl Test

03

Use terminal to check server headers directly, bypassing browser cache.

curl -I "https://site.com/?gclid=test"

Check the 'Location' header in the response.

04. Code-Level Remediation

The solution depends on your tech stack. The goal is always to capture the query string on the incoming request and append it to the redirect target.

Nginx (Server Block)

# Standard Redirect (BAD) rewrite ^/old-page$ /new-page permanent;# Preserving Arguments (GOOD) # Method 1: $args rewrite ^/old-page$ /new-page?$args permanent;# Method 2: try_files (WordPress Standard) try_files $uri $uri/ /index.php?$args;

Apache (.htaccess)

# Enable Rewrite Engine RewriteEngine On# The QSA (Query String Append) flag is CRITICAL RewriteRule ^old-page/?$ /new-page/ [R=301,L,QSA]

WordPress (functions.php)

// Whitelist Google Query Params if filtered add_filter('query_vars', function($vars) { $vars[] = 'gclid'; return $vars; });

Complexity vs. Reliability of Fixes

Comparison of methods to resolve GCLID stripping. Server-level fixes are generally more robust than plugin-based solutions.

05. The Cost of Inaction

Impact on Data Accuracy & Revenue Attribution

Without Fix: Blind Spots

When GCLID is stripped, Google Analytics treats the session as "Direct/None" or "Organic". You lose campaign, keyword, and ad group data. ROAS calculations become impossible.

With Fix: 99% Attribution

Preserving the parameter ensures the click is stitched to the session. This enables:

  • Accurate ROAS reporting
  • Enhanced Conversions functionality
  • Audience remarketing population

ENGINEER NOTES: SUMMARY

  • > Always test redirects with `curl -I` before standardizing.
  • > Avoid daisy-chaining redirects (A->B->C); breaks tracking easily.
  • > If using Cloudflare, check "Page Rules" for strip query string settings.
  • > Server-Side Tagging (GTM) is the ultimate fail-safe for params.

Google Ads Tracking Fix

Fix GCLID Stripping on Redirect Long Form

The Google Click Identifier (GCLID) is the “receipt” that connects an ad click to a conversion. When a redirect drops ?gclid=... (or wbraid / gbraid), Google Ads can’t match conversions back to clicks. Smart Bidding learns the wrong lessons, GA4 sessions drift into “Direct,” and offline conversion uploads fail.

What to preserve

Do not only preserve gclid. Also allowlist:

  • gclid (web click ID)
  • gclsrc (source helper)
  • wbraid (web-to-app, privacy)
  • gbraid (app-to-app, privacy)
  • • UTMs (optional, but usually needed)

Why it breaks

Redirects are 3xx responses with a Location header. If the redirect target URL does not include the original query string, your IDs are dropped.

Redirect chains raise the odds: http → https → www → trailing slash → app routing. One bad hop loses the IDs forever.

Why it matters

  • • Paid clicks show as “Direct” in GA4.
  • • Smart Bidding under-bids high value keywords.
  • • Offline conversion import can’t match sales to clicks.

If your GA4 and Ads still don’t align after fixes, use a proof baseline: GA4 and Google Ads attribution alignment .

Diagnose the exact hop where parameters disappear

Your goal is simple: find the first redirect whose Location header does not include the query string.

Step 1

Smoke test in the browser

https://yourdomain.com/landing?gclid=TEST_123&wbraid=TEST_456

If the page loads and the address bar drops the params, stripping is active.

Step 2

Chrome DevTools (redirect chain)

  • • Network tab → enable “Preserve log”
  • • Load the test URL
  • • Click the first 301/302
  • • Check Response Headers → Location

Step 3

curl (server truth)

curl -I -L -v "https://yourdomain.com/landing?gclid=TEST_123"

Watch each Location: and find where the query string drops.

If you see 403/406 errors: it may be a WAF/security block, not a redirect strip. Check Wordfence/Solid Security logs and allowlist gclid/wbraid/gbraid.

Fixes by layer (start at the top)

The pattern is always the same: capture the incoming query string and append it to the redirect target. Then add a safety net so a future change doesn’t break it again.

1) Google Ads Final URLs: stop triggering redirects

  • • Make Final URLs match your canonical format (https, www vs non-www, trailing slash).
  • • Avoid extra hops through old domains, link shorteners, or “helper” redirects.
  • • Fewer hops = fewer failure points.

2) Nginx: preserve args explicitly

Best practice: $is_args$args

# Redirect with argument preservation
rewrite ^/old-path$ /new-path$is_args$args permanent;

# Trailing slash enforcement (preserving args)
rewrite ^([^.]*[^/])$ $1/$is_args$args permanent;

# WordPress try_files: DO NOT drop args
# Bad: try_files $uri $uri/ /index.php;
try_files $uri $uri/ /index.php$is_args$args;

3) Apache (.htaccess): use QSA

RewriteEngine On

# BAD: can strip existing query string
RewriteRule ^old-page/?$ /new-page/ [R=301,L]

# GOOD: preserves query string (QSA)
RewriteRule ^old-page/?$ /new-page/ [R=301,L,QSA]

4) WordPress: protect canonical redirects

WordPress can issue “clean” canonical redirects that drop unknown query parameters. Add a filter that re-attaches the tracking params.

/**
 * Preserve attribution parameters during WordPress canonical redirects.
 */
function preserve_tracking_parameters( $redirect_url, $requested_url ) {
  $tracking_params = [
    'gclid','wbraid','gbraid','gclsrc',
    'utm_source','utm_medium','utm_campaign','utm_term','utm_content'
  ];

  $parsed = parse_url( $requested_url );
  if ( empty( $parsed['query'] ) ) return $redirect_url;

  parse_str( $parsed['query'], $query );
  $keep = [];

  foreach ( $tracking_params as $p ) {
    if ( isset( $query[$p] ) ) $keep[$p] = $query[$p];
  }

  if ( ! empty( $keep ) ) $redirect_url = add_query_arg( $keep, $redirect_url );
  return $redirect_url;
}
add_filter( 'redirect_canonical', 'preserve_tracking_parameters', 10, 2 );

5) CDN + WAF: the “hidden strippers”

  • • In Cloudflare redirect/transform rules, ensure “Preserve query string” is enabled.
  • • If APO or caching ignores query strings, consider bypassing cache when gclid/wbraid/gbraid are present.
  • • In security plugins (Wordfence/Solid Security), allowlist those params to prevent false positives.

6) Defense in depth: capture on first touch

Even after you fix redirects, capture IDs immediately and store them. This protects you from future config changes.

(function() {
  function getParam(p) {
    var m = RegExp('[?&]' + p + '=([^&]*)').exec(window.location.search);
    return m && decodeURIComponent(m[1].replace(/\+/g,' '));
  }

  function setCookie(name, value, days) {
    var d = new Date();
    d.setTime(d.getTime() + (days*24*60*60*1000));
    document.cookie = name + '=' + value +
      ';expires=' + d.toUTCString() +
      ';path=/;SameSite=Lax;Secure';
  }

  var keys = ['gclid','gclsrc','wbraid','gbraid'];
  keys.forEach(function(k) {
    var v = getParam(k);
    if (v) {
      setCookie(k, v, 90);
      localStorage.setItem(k, v);
    }
  });
})();

If you need those IDs in a CRM or offline pipeline, see: offline conversions and CRM uploads .

7) WooCommerce: store IDs on the order for offline conversion import

Save gclid/wbraid/gbraid from cookies to order meta at checkout. Then you can export and upload conversions later.

add_action( 'woocommerce_checkout_create_order', 'save_attribution_data_to_order', 10, 2 );
function save_attribution_data_to_order( $order, $data ) {
  $keys = ['gclid','wbraid','gbraid'];
  foreach ( $keys as $key ) {
    if ( isset( $_COOKIE[ $key ] ) ) {
      $value = sanitize_text_field( $_COOKIE[ $key ] );
      $order->update_meta_data( '_' . $key, $value );
      $order->update_meta_data( $key, $value );
    }
  }
}

If GA4 purchase/revenue is also off, fix it in parallel: GA4 purchase and revenue mismatches (WooCommerce) .

Validate the fix

Validate in three places: browser behavior, server headers, and platform reporting.

Browser

  • • Address bar keeps params after redirects
  • • DevTools shows Location includes query string

Server

  • curl -I -L keeps query string on every hop
  • • No double-appended query strings

GA4 + Google Ads

  • • Paid traffic stops leaking into “Direct”
  • • Conversion diagnostics improve over time
  • • Offline upload match rates increase (if used)

Want this fixed end-to-end?

We’ll trace the redirect chain, patch server/CMS/CDN rules, and verify attribution alignment.