Redirects, Analytics, and Attribution: What Actually Breaks Your Tracking (And How to Fix It)
Redirects are supposed to be invisible. They move users from one URL to another without friction, preserve old links, and quietly keep campaigns running. But in practice, they are one of the most reliable ways to destroy attribution data without anyone noticing until it's too late.
I've debugged this exact problem across multiple short-link deployments, including on vvd.im. What I found consistently surprised me: the damage rarely looks like damage. It looks like unusually high direct traffic, mysteriously underperforming campaigns, and conversion paths that seem to start from nowhere.
This guide is not a beginner's introduction to UTMs. It assumes you already know what UTM parameters are and why they matter. What it covers instead is the specific failure modes that even experienced marketers and developers miss — and the concrete steps to fix them at the infrastructure level.
The Real Cost of Redirect-Related Attribution Loss
Before getting technical, it helps to understand the scale of the problem. When a redirect drops UTM parameters or referrer data, the analytics impact is not just a missing row in a report. It compounds across the entire attribution model.
Consider a campaign that generates 10,000 clicks through a short link. If the redirect silently strips UTM parameters on 15% of those clicks — a realistic number in misconfigured setups — you lose attribution on 1,500 sessions. Those sessions get classified as Direct. Your campaign ROAS looks lower than it is. Budget decisions get made on corrupted data. Over a quarter, that compounds into meaningful misallocation.
The insidious part is that nothing looks broken. Pages load. Conversions fire. Reports populate. The data is just quietly wrong.
How Referrer Headers Actually Work Across Redirects
Most documentation explains that cross-domain redirects can drop referrer data. What it rarely explains is why — and understanding the why is what lets you actually fix it.
When a browser follows a redirect, the Referer header it sends to the destination depends on two things: the Referrer-Policy of the originating page, and whether the redirect crosses a domain or protocol boundary.
By default, most browsers implement the strict-origin-when-cross-origin policy. This means:
- Same-origin navigation: full URL is sent as referrer
- Cross-origin navigation (HTTPS to HTTPS): only the origin is sent (no path, no query string)
- Downgrade (HTTPS to HTTP): no referrer is sent at all
In a short-link context, this matters enormously. A redirect from vvd.im/abc123 to example.com/landing crosses a domain boundary. Analytics on example.com sees the referrer as vvd.im — not the original source that drove the click. That original source is already gone by the time the redirect fires.
Where the Original Source Actually Lives
The original source — the email, the social post, the paid ad — exists in two places: the UTM parameters appended to the short link, and the referrer of the page that contained the short link.
The referrer of the linking page is already lost the moment the user clicks the short link. Analytics on vvd.im sees it, but analytics on example.com never will. This is why UTM parameters on the short link itself are not optional — they are the only reliable mechanism for carrying campaign intent across a domain boundary.
UTM Parameter Failure Modes: A Taxonomy
UTMs fail in predictable ways. Each failure mode has a different cause and a different fix.
1. Query String Not Forwarded by Redirect Rule
This is the most common failure. A redirect rule maps one URL to another but forgets to append the query string. The user arrives at the destination with clean parameters — and no attribution.
In Nginx, the difference looks like this:
# Broken: drops query string
rewrite ^/abc123$ https://example.com/landing redirect;
# Correct: preserves query string
rewrite ^/abc123$ https://example.com/landing$is_args$args redirect;
The $is_args$args variable appends the original query string to the destination URL. Without it, every redirect silently strips UTMs.
In Spring Boot, if you're building a redirect service:
@GetMapping("/{shortCode}")
public ResponseEntity<Void> redirect(
@PathVariable String shortCode,
HttpServletRequest request) {
String destination = resolveDestination(shortCode);
String queryString = request.getQueryString();
// Preserve original query string from the incoming request
if (queryString != null && !queryString.isEmpty()) {
destination = destination + (destination.contains("?") ? "&" : "?") + queryString;
}
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY)
.location(URI.create(destination))
.build();
}
Note that this preserves UTMs attached to the short link itself. It does not reconstruct UTMs that were never there to begin with.
2. Redirect Chain Overwrites Parameters
Redirect chains are common in real deployments. A short link redirects to a tracking URL, which redirects to a landing page, which redirects to a localized version. Each hop is an opportunity for parameters to be overwritten or dropped.
The specific failure mode here is when an intermediate redirect has its own query parameters that conflict with or replace the originals. When the final destination receives the URL, it sees the intermediate parameters, not the campaign parameters.
The only reliable fix is to audit every hop in the chain. Use curl with the -L flag to follow redirects and inspect what the final URL looks like:
curl -Ls -o /dev/null -w "%{url_effective}" \
"https://vvd.im/abc123?utm_source=email&utm_medium=newsletter&utm_campaign=q1"
The output should show the final destination URL with all UTM parameters intact. If the output URL has no UTMs, the chain is broken somewhere.
3. JavaScript Redirect Race Conditions
JavaScript redirects — window.location.href assignments or meta-refresh tags — create a timing problem that server-side redirects do not have.
Analytics scripts (GA4, etc.) are loaded asynchronously. When a JavaScript redirect fires, there is no guarantee that the analytics library has finished initializing or sending its pageview event. If the redirect fires first, the pageview is abandoned mid-flight.
The result is intermittent tracking loss. Not every session. Not every visit. Just enough to make the data unreliable without making the bug obvious.
If you must use a JavaScript redirect — for example, on a page that needs to run client-side logic before redirecting — the safer pattern is to fire analytics events explicitly before redirecting, or use the beacon transport to ensure the event sends even as the page unloads:
// GA4: use beacon transport and wait for confirmation
gtag('event', 'redirect_fired', {
event_callback: function() {
window.location.href = destinationUrl;
},
event_timeout: 2000 // fallback: redirect anyway after 2s
});
The event_callback fires after GA4 confirms the event was sent. The timeout ensures the redirect happens even if GA4 fails to respond.
GA4 Referral Exclusion: The Fix That Most Teams Miss
Even when UTMs are preserved correctly, cross-domain redirects create a second problem in GA4: the short domain appears as a referral source in the destination property, which can reset the session and misattribute the conversion.
The fix is referral exclusion. In GA4:
- Go to Admin → Data Streams → Configure tag settings
- Under "More Settings," find List unwanted referrals
- Add your short domain (e.g., vvd.im) to the exclusion list
With this configured, GA4 will not start a new session when traffic arrives from your short domain. Instead, it continues the existing session with the original attribution intact.
This is critically important if your short domain ever appears as a referrer in destination reports. Without this exclusion, vvd.im effectively becomes a "source" that steals credit from every campaign it touches.
Cross-Domain Tracking: When Cookies Are Not Enough
If your short domain hosts content — as vvd.im does — and you want to track users continuously as they move from vvd.im content into a separate domain, standard analytics setup will break the session at the domain boundary.
GA4 handles this with cross-domain measurement. The configuration is straightforward, but the implications are not:
- Go to Admin → Data Streams → Configure tag settings → Configure your domains
- Add both domains that should share session state (e.g., vvd.im and example.com)
When configured correctly, GA4 appends a _gl parameter to links that cross the domain boundary. This parameter carries the client ID, allowing GA4 to stitch the session together on the receiving domain.
The catch: this only works for links that GA4 can instrument — typically anchor tags on pages where the GA4 snippet is loaded. A direct server-side 301 redirect bypasses this mechanism entirely. For server-side redirects across domains, UTM parameters on the short link remain the only reliable attribution mechanism.
Testing Attribution Like an Engineer, Not a Marketer
Most redirect testing ends at "does the page load correctly." That tells you nothing about attribution. Proper attribution testing requires verifying the full data path, not just the user-visible outcome.
The Test Protocol
For each redirect path you operate, run this verification sequence:
-
Construct a test URL with known UTM parameters:
https://vvd.im/testcode?utm_source=test&utm_medium=email&utm_campaign=redirect_audit -
Follow the redirect chain manually using curl:
curl -Ls -o /dev/null -w "%{url_effective}" [test URL]
Verify UTMs appear in the final URL. - Open the final URL in a browser with GA4 DebugView active (Admin → DebugView). Verify the pageview event fires with the correct campaign parameters.
- Check the session source in real-time reports. Confirm the session is attributed to the test campaign, not to Direct or vvd.im as a referral.
This sequence takes about five minutes per redirect path. It is significantly less time than debugging attribution problems in production after a campaign has run.
Automating the Check
For redirect services with many active short codes, manual testing is impractical. A simple automated check can verify UTM propagation across the most important redirect paths:
#!/bin/bash
# redirect-utm-check.sh
# Tests that UTMs survive redirect chain
SHORT_CODES=("abc123" "def456" "ghi789")
TEST_UTM="?utm_source=audit&utm_medium=test&utm_campaign=redirect_check"
BASE_URL="https://vvd.im"
for code in "${SHORT_CODES[@]}"; do
final_url=$(curl -Ls -o /dev/null -w "%{url_effective}" \
"${BASE_URL}/${code}${TEST_UTM}")
if echo "$final_url" | grep -q "utm_source=audit"; then
echo "PASS: $code → UTMs preserved"
else
echo "FAIL: $code → UTMs lost. Final URL: $final_url"
fi
done
Run this as part of your deployment pipeline or as a scheduled job. When a redirect configuration change drops UTM parameters, you find out before the next campaign launches — not after.
The Architecture Decision That Changes Everything
Most of the problems in this guide come from the same architectural pattern: a short domain that exists purely to redirect, with no analytics context of its own.
When a domain hosts real content — as vvd.im does — it can maintain its own analytics session, set its own cookies, and carry context forward into redirects in ways that pure redirect domains cannot. This is not just an SEO advantage. It's an attribution advantage.
A page on vvd.im that displays content before redirecting can fire analytics events, capture the referrer of the inbound click, and encode that information into the outbound URL as UTM parameters. A pure redirect domain cannot do any of this — it forwards the request before any client-side code has a chance to run.
This architectural difference is why interstitial pages — pages that briefly show content before forwarding users — are more common in professional link management systems than they appear. They are not just for showing ads. They are a mechanism for preserving attribution context that server-side redirects cannot capture.
Summary: What to Actually Do
The steps below are in order of impact. Start with the first and work down.
- Audit your Nginx or application redirect rules to confirm $is_args$args (or equivalent) is appended to every redirect target. This single change fixes the most common UTM loss.
- Add your short domain to GA4 referral exclusions on every property that receives traffic from it. This prevents session resets and credit theft.
- Run the curl test on your most important short codes. Verify UTMs appear in the final URL before the next campaign launches.
- Replace JavaScript redirects with server-side redirects wherever possible. Where you cannot, use the event_callback pattern before redirecting.
- Configure cross-domain measurement if you need session continuity across domains. Understand its limitations with server-side redirects.
Redirects that respect attribution are not complicated to build. They require deliberate configuration rather than default behavior. The default behavior, unfortunately, is to quietly corrupt your data.
Users will never notice. Dashboards will look populated. The only sign something is wrong is campaigns performing worse than they should, and a stubborn spike in Direct traffic that nobody can explain.
Now you can explain it — and fix it before it costs you.