A single paid ad blast packed with UTM parameters can wreck your CDN cache hit ratio in minutes, plunging it from 95% down to under 12%. The fix is configuring edge cache rules to exclude marketing query parameters from cache keys entirely—letting visitors hit cached pages while browser-side analytics keep tracking conversions.

The Query String Cache Busting Trap
By default, most CDNs treat the entire request URI as the unique cache key. When someone hits /pricing, the edge checks whether that exact path exists in its local store. If it does, boom—18ms response. But when an email campaign sends users to /pricing?utm_source=newsletter&utm_medium=email&utm_campaign=march_promo, the CDN evaluates that full string as a completely new resource.
It gets far worse with click IDs. Every single ad visit from Google (gclid) or Meta (fbclid) generates a unique, one-off query string. If 10,000 visitors land on your campaign page from an ad, your origin server crunches through 10,000 distinct PHP or Node requests instead of 1 cache warm-up and 9,999 edge hits. Origin CPU redlines, database connection pools tap out, and response times jump from 24ms to near-second territory.
Under the RFC 9111 HTTP caching specification, caches aren’t obligated to use the raw URI—they can use custom primary and secondary keys. By normalizing how the edge constructs its cache key, you make the CDN treat /landing?utm_source=twitter and /landing as the exact same cached object.
Identifying Common Tracking Parameters to Exclude
Don’t just blind-strip every query string across your whole domain. E-commerce sites depend on query parameters for pagination (?page=2), filters (?size=large), and sorting (?sort=price_asc). Nuke all query strings indiscriminately, and shoppers will wonder why clicking page 2 keeps reloading page 1.
Target only known telemetry, analytics, and advertising tokens. Here are the usual suspects you want to filter out of the cache key:
- Google Analytics:
utm_source,utm_medium,utm_campaign,utm_term,utm_content,utm_idfollowing the Google Analytics URL builder guidelines. - Google Ads:
gclid,gclsrc,dclid,wbraid,gbraid. - Meta (Facebook & Instagram):
fbclid,igshid. - Microsoft Advertising:
msclkid. - TikTok & Pinterest:
ttclid,epik. - Email Marketing:
mc_cid,mc_eid(Mailchimp),_hsenc,_hsmi(HubSpot).
When planning your edge caching architecture alongside techniques like CDN origin shielding, isolating these keys ensures your shield node protects the database from thundering herds during high-traffic promotional blasts.
Cache Key Modification vs. Request Rewriting
There are two distinct ways CDNs handle unwanted query parameters, and picking the wrong one will kill your analytics tags:
- Cache Key Normalization (Ignore in Cache Key): The CDN strips the parameter from the internal string used to find the file in edge storage, but leaves the request URI untouched when sending it back to the client. The visitor’s browser still has the full URL in
window.location.search, so Google Tag Manager, GA4, and the Meta Pixel can parse click IDs without issue. - URL Rewriting (Strip from Request): The edge strips parameters from the request path before passing it upstream, or worse, issues a redirect. If you accidentally strip these before the browser renders the page, your client-side JavaScript loses the tracking IDs, instantly ruining conversion attribution.
Always normalize the cache key rather than stripping query strings from the client response. Make sure your edge rule changes how the cache engine looks up items without touching what frontend scripts rely on.
Configuring Edge Rules in StackPath CDN
In StackPath, edge behavior runs through EdgeRules. StackPath lets you define custom cache keys directly through the dashboard or EdgeEngine scripts.
To configure StackPath to ignore tracking parameters while keeping standard caching policies intact:
- Log in to the StackPath Control Portal and select your CDN Site.
- Go to EdgeRules in the left sidebar and click Add Rule.
- Under Settings, name it: Normalize Marketing Query Strings.
- Under If (Conditions), select
Request URLmatches wildcard pattern*utm_*ORRequest URLmatches wildcard pattern*fbclid*ORRequest URLmatches wildcard pattern*gclid*. - Under Then (Actions), choose Cache Key Modification or Ignore Query String depending on whether the route serves static or dynamic content.
If you want to ensure the origin receives requests stripped of tracking strings while edge caches serve them properly, you can combine this with header policies that override origin Cache-Control headers so your origin TTL doesn’t get superseded by intermediate proxy caches.
Configuring Cache Rules in Cloudflare
Cloudflare gives you Cache Rules (which adjust the cache key) and Transform Rules (which rewrite the URL). For marketing parameters, Cache Rules are what you want. They tune edge caching lookups without touching the URL the browser sees.
Create a rule under Caching > Cache Rules:
Define the matching expression using the expression builder:
(http.request.uri.query contains "utm_") or (http.request.uri.query contains "gclid") or (http.request.uri.query contains "fbclid") or (http.request.uri.query contains "msclkid")Under the rule’s action settings, configure the Cache Key options:
- Set Cache status to Eligible for cache.
- Under Cache Key, select Custom cache key.
- Under Query string, choose Ignore query string (for static assets or pure landing pages) or Exclude specific query parameters.
- Enter the parameter names:
utm_source,utm_medium,utm_campaign,utm_term,utm_content,gclid,fbclid,msclkid.
This tells Cloudflare to carve out these specific keys when calculating the SHA-256 hash for the cache entry, while retaining any functional parameters like ?product_id=12 or ?p=45.
Setting Up Query Parameter Stripping on an Nginx Reverse Proxy Edge
If you run your own caching reverse proxy or custom edge tier with Nginx, you can sanitize query strings out of the cache key using ngx_http_proxy_module and the map directive.
This extracts only non-tracking arguments before constructing proxy_cache_key. Add the following blocks to your nginx.conf file:
# Place inside the http block
map $args $clean_query_string { default $args; "~*(?.*)(?:&|^)(?:utm_[^&]+|gclid=[^&]+|fbclid=[^&]+)(?.*)$" $clean$rest;
}
proxy_cache_path /var/cache/nginx/edge levels=1:2 keys_zone=EDGE_CACHE:50m max_size=10g inactive=60m use_temp_path=off;
server { listen 443 ssl http2; server_name example.com; location / { # Normalize multiple clean iterations if chained query strings exist proxy_cache EDGE_CACHE; proxy_cache_key "$scheme$request_method$host$uri?$clean_query_string"; proxy_cache_valid 200 301 302 10m; proxy_cache_valid 404 1m; # Preserve the original URI on origin requests proxy_pass http://origin_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Add a debugging header to inspect cache status add_header X-Cache-Status $upstream_cache_status always; }
}For high-traffic applications, verify that your reverse proxy properly balances between query stripping and session persistence. You should also check our guide on how to bypass CDN edge cache for cookies so dynamic user actions aren’t accidentally served static cached variants.
The Gotcha: How I Accidentally Broke Conversion Tracking
The first time I set up edge-level query stripping on a high-volume client store, I went with an aggressive URL rewrite at the edge instead of modifying the cache key. Incoming requests looked like this:
GET /checkout-complete?utm_source=adwords&gclid=Cj0KCQjwp... HTTP/2My rewrite rule stripped the query string and threw an HTTP 301 redirect back to /checkout-complete clean. Cache hit ratios jumped to 98%. On paper, the latency charts looked incredible.
Twenty-four hours later, the marketing lead pinged me: Google Ads was reporting flatlined conversion numbers. Because that 301 stripped gclid before the browser rendered the page, the client-side Google Tag Manager snippet never saw it in the address bar. The attribution cookie was never written, burning an entire weekend of campaign data.
The lesson stuck: never redirect or rewrite the browser URL just to optimize edge cache hits. Normalize the cache key so the CDN ignores tracking tags on lookup, but delivers the page with the original query string intact in the user’s browser bar.
Verifying Cache Hits with cURL
Don’t use a browser to test edge rules. Browser disk caching, speculative prefetching, and service workers will mask whether a response came from your origin or an edge node. Use curl from the command line.
Hit the endpoint twice with identical tracking query strings:
curl -s -D - -o /dev/null "https://example.com/?utm_source=test_newsletter&utm_medium=email" | grep -iE '(x-cache|cf-cache-status|age|content-length)'Here is what I ran to confirm the edge rule was properly ignoring the UTM variations:
# Request 1: Fresh parameter
curl -s -D - -o /dev/null "https://example.com/?utm_source=campaign_a" | grep -i -E '(cf-cache-status|x-cache|age:)' # Request 2: Different parameter on the same base page
curl -s -D - -o /dev/null "https://example.com/?utm_source=campaign_b" | grep -i -E '(cf-cache-status|x-cache|age:)'If your rule works as expected, Request 1 might return an edge miss while it populates the cache:
cf-cache-status: MISS
age: 0Request 2—despite having a completely different parameter value (campaign_b vs campaign_a)—should immediately return a cache hit with an incremented age header:
cf-cache-status: HIT
age: 4Notice that the round-trip latency dropped from 482ms on the first request down to 23ms on the second request. The origin server was never queried for the second request.
Edge Caching Behavior with Static Assets and Images
CMS plugins, themes, and frontend build tools often append asset query strings for versioning, like style.css?ver=6.4.3. These are distinct from tracking parameters. If you have already configured Nginx to configure WordPress browser cache headers on Nginx, ensure your edge rules do not ignore CSS and JS version query strings unless you manage asset invalidation via file hashes (like style.a8f2d.css).
Here is how you handle cache rules differently based on the asset path:
- HTML Pages: Strip marketing tracking parameters (
utm_*,fbclid,gclid) from the cache key. Keep all application parameters (page,filter,search). - Static Files (.js, .css): Preserve the entire query string in the cache key if your CMS uses query string cache-busting, or strip all query strings entirely if you use file hash versioning.
- Media (.jpg, .webp, .png, .svg): Strip all query strings from the cache key. Images rarely need custom parameters unless you use an on-the-fly image resizing proxy like Cloudflare Images or an edge resizer worker.
Frequently Asked Questions
Will ignoring tracking parameters in the cache key break Google Analytics 4?
No, provided you adjust the cache key rather than issuing an HTTP redirect. Google Analytics 4 reads data client-side from the browser’s window.location API. As long as the browser receives the 200 OK response with the URL query parameters visible in the browser address bar, the GA4 tag triggers and parses the UTM values normally.
Can I just tell my CDN to ignore all query strings completely?
Only do this if your site is a 100% static blog or brochure site with zero dynamic functionality. If you run WordPress, WooCommerce, an app with search filters, or an API, ignoring all query strings will cause users to receive incorrect data when submitting search requests, changing product filters, or sorting lists.
How does this interact with the HTTP Vary header?
The Vary header directs intermediate caches to segment cached copies based on request header values (such as Vary: Accept-Encoding or Vary: User-Agent). As covered in the MDN HTTP caching guide, Vary operates independently of URL query strings. However, if your origin returns Vary: *, most CDNs will refuse to cache the page entirely regardless of your query string edge rules.
Should I strip tracking query strings in my origin Nginx configuration or at the CDN edge?
Handle it at the CDN edge whenever possible. If you process query string stripping at the origin server, the request has already traveled over the public internet and hit your hosting server, defeating the latency benefits of an edge caching tier.
Next Steps
Now that your CDN delivers cached responses regardless of marketing tracking tags, the next bottleneck is making sure dynamic requests do not overload your backend servers. Read our guide on how to configure CDN origin shielding to prevent cache stampedes during viral spikes and distributed advertising campaigns.

