pixellint

Pixels · engineers · marketers

A 200 in a click chain is a dead end

Clicks are supposed to 302 until the landing page. If a hop returns 200 with an HTML body, the user stops there. If a hop 302s but strips the query, the landing page looks fine and attribution does not. curl -sIL shows every Location. A browser tab does not.

Read the Location header hop by hop

Debug with a tool that shows hop-by-hop status, not with a browser that follows redirects and only shows the last document. curl -sIL is enough. Confirm each Location still carries the click id you need. Count hops. A chain that was three hops last week and is twelve hops after a tag vendor wrap is how timeouts and dropped params start.

Look at the method. A 302 on GET is normal. A 307 preserves method; a 308 is permanent with method preserved. A 301 that your CDN cached without the query string will keep eating gclid until you purge. Core flags userinfo in the URL and fragments that never reach the server; it will not walk the chain for you. You walk it.

200 mid-chain is a dead end

The user should see one document: your landing page, status 200. Every prior hop should be 3xx with Location. A 200 in the middle means that hop served HTML (an interstitial, a cookie-sync page that forgot to redirect, a parked domain, an expired click tracker). The chain stops. Downstream pixels on your thank-you page never run because the user never arrived.

A 404 or 410 mid-chain is the same class of failure with a sadder status. A 500 is a vendor outage. A 302 to the same URL is a loop; curl -sIL will repeat until it gives up. Mixed content is quieter: an http image or tracker on an https page may not fire at all. Core flags http for upgrade (core.url.insecure_transport) and unsupported schemes. It will not detect a redirect loop. Watch hop count.

http to https drops the query

The most boring outage in paid search is an http://www.example.com/lp?gclid=... 301 to https://www.example.com/lp with no query. The landing page looks fine. Google Ads shows clicks. GA4 shows Direct. Enhanced conversions have nothing to join. The fix is a redirect that forwards the query string, configured on the CDN or the app, not a new pixel.

The same bug happens on HTTPS upgrades inside the chain: an ad server that 302s to http://vendor.example/... and the vendor upgrades to https without forwarding Location's query. You lose fbclid the same way you lose gclid. Test with a real click id on the first URL, not with a naked landing URL you typed into the address bar.

www, apex, trailing slash, and case

example.com to www.example.com, or the reverse, is a second place query strings die. Trailing-slash redirects are a third. Path case ( /LP vs /lp ) is a fourth on case-sensitive origins. Each is a 301 someone configured in a hurry. Each should copy the query. None of them look like a pixel outage in Events Manager; they look like a Direct spike.

Host-only cookies set on www do not travel to apex. If the click lands on apex and the pixel fires on www after a redirect that did keep gclid, you may still lose _ga or _fbp written on the first host. Align the canonical host, the cookie domain, and the redirect map before you rebuild the tag.

What a clean chain looks like

Publisher 302 to ad server with their click id. Ad server 302 to vendor stamper, appending gclid or fbclid. Stamper 302 to your landing URL with the click id still in the query. Your origin 200. Then your page writes the click id into a first-party cookie and fires PageView. That is the whole product. Anything else is a join you will do in a spreadsheet.

Run the curl below and read every HTTP status and Location. If you see 200 before the landing host, stop and fix that hop. If gclid is on hop 1 and missing on hop 3, stop and fix that hop. Do not add a second click tracker to compensate. Core will still want an absolute https URL with a host and no userinfo on each Location you then paste into pixellint validate url.

curl -sIL 'https://ad.example/clk?gclid=Cj0KCQjwTEST'

Check the artifact

Paste the pixel URL or JSON body into the playground. Same engine as pixellint validate. Nothing leaves the tab.