Page speed, redirects, Core Web Vitals
Fix redirects that strip query parameters
Redirects that drop ?utm_ and other query strings break analytics, attribution, and deep linking. Configure rules to preserve query parameters across the hop.
What's happening
When a 301/302 rule rewrites the URL but doesn't preserve the query string, every redirect hop strips?utm_source,?ref,?gclid, and any other tracking or app-specific parameters. The destination receives the bare path, attribution data is lost, and deep links break. Marketing teams discover this when campaign UTMs stop appearing in analytics.
Curl confirms the behavior: curl -I 'https://oldsite.com/path?utm_source=google' shows the Location header. If Location is just /path or https://newsite.com/path without the query string, the redirect is stripping params. CheckFast's /redirects tool runs the same check and flags strip-on-redirect explicitly.
The fix depends on the layer issuing the redirect. nginx, Apache, Vercel, Cloudflare, and most frameworks have specific syntax to preserve query parameters during redirect — typically a $request_uri variable, or a destination format that includes the query string explicitly.
Why it matters
Analytics breaks first. UTM parameters from email campaigns, ad platforms, and partner referrals never reach GA, Plausible, or your event pipeline. Attribution data — what's driving traffic and conversions — disappears for any URL that goes through a redirect.
Application deep links break second. App-specific params (?step=2&token=abc,?invite=xyz) that should land users in a specific state get dropped, leaving users on a generic page without context. Conversion funnels degrade silently because the redirect destination behaves differently than the original URL did.
Common causes
- nginx return 301 https://newsite.com/path; instead of return 301 $request_uri.
- Apache RedirectMatch with a literal destination that doesn't include the query string.
- Vercel/Netlify redirect rules with a destination path that doesn't end in:slug* or the equivalent capture.
- Application-level redirect using res.redirect('/new-path') without including req.url's search component.
- URL rewriting rules that explicitly drop the query string.
- Affiliate or tracking proxy redirects that re-build URLs without forwarding params.
Detect this on your site
Run a quick scan with the Redirect Checker. The tool surfaces this exact issue with the records and context needed to apply the fix below.
Open Redirect CheckerHow to fix it
- 1
Test redirects with query strings
Run curl -ILv 'https://yoursite.com/path?utm_source=test' against your most-redirected URLs. Compare the Location headers against the request URL. The query string should appear in every Location.
- 2
Use $request_uri in nginx
Change return 301 https://newsite.com/path; to return 301 https://newsite.com$request_uri;. The $request_uri variable includes the path and query string verbatim. For path rewrites, use return 301 https://newsite.com/new-path?$query_string;.
- 3
Use [QSA] flag in Apache
RewriteRule ^/old-path /new-path [R=301,L,QSA]. The QSA (query string append) flag tells mod_rewrite to keep the original query string. Without it, Apache may drop the query depending on the rule pattern.
- 4
Preserve params in Vercel redirects
vercel.json: { source: "/old/:path*", destination: "/new/:path*", permanent: true }. The:path* wildcard preserves the path and query. For static destinations, use the {q} interpolation: destination: "/new?{q}".
- 5
Preserve params in framework redirects
Express: res.redirect(301,
/new-path${req._parsedUrl.search ?? ''}. Next.js next.config.js: same pattern as Vercel rewrites. Always concatenate the original query string onto the new path. - 6
Document allowlisted parameter strip
Some redirects intentionally strip params (e.g., a canonical-URL normalization that drops?ref=). For these, document why, and consider preserving utm_*, gclid, fbclid even when stripping app-specific params, so analytics still works.
- 7
Test the fix end-to-end
After deploying, re-run curl with a query string and verify it survives every hop. Check analytics 24 hours later — UTM parameters should appear in the campaign reports for traffic that traversed the redirect.
Example
# Bad: drops the query string
location /old-path {
return 301 /new-path;
}
# Good: preserves path and query
location /old-path {
return 301 /new-path?$query_string;
}
# For full domain migration, $request_uri preserves both path and query
server {
server_name oldsite.com;
return 301 https://newsite.com$request_uri;
}
# Vercel example (vercel.json):
{
"redirects": [
{
"source": "/old-path",
"destination": "/new-path",
"permanent": true,
"has": [{ "type": "query", "key": "utm_source" }]
}
]
}$request_uri (nginx) and:path* (Vercel) preserve query strings on redirect.
Frequently asked
Tracking params (utm_ gclid) don't affect ranking but do affect analytics. Session/state params (?session=abc) can create duplicate content if not canonicalized. Use rel= canonical to point parameter-bearing URLs at the clean canonical.
At the application layer parse the query string drop the unwanted keys rebuild the URL and redirect. Pure web-server config (nginx Apache) can't easily do conditional param-by-param logic — it's all-or-nothing.
Yes for canonicalization (?ref=foo doesn't change the page content) and for security (auth tokens shouldn't survive in browser history after redirect). Strip surgically not wholesale.
Related fixes
Page speed, redirects, Core Web Vitals
Reduce too many redirects: cut chains to one hop
Page speed, redirects, Core Web Vitals
Fix redirect loop: break circular 301/302 chains
Page speed, redirects, Core Web Vitals
Reduce redirect chain length: collapse multi-hop chains
Page speed, redirects, Core Web Vitals
Fix missing www/non-www redirect to canonicalize the host