pixellint

Pixels · engineers · marketers · PMs

The tag ran. The request did not.

uBlock Origin, AdGuard, Brave, and Safari's tracker prevention share lists of collector hosts. facebook.com/tr, google-analytics.com/g/collect, and ads.tiktok.com are not subtle. GTM preview is a JavaScript callback. The Network tab is the request. Believe the Network tab.

How it looks in QA

GTM preview says the tag fired. The vendor's test-events UI is empty. Chrome with no extensions shows the hit. That is not a race; that is a list match on the host. uBlock's logger will name the filter. EasyList and EasyPrivacy are the usual lists; Fanboy and regional lists add more hosts. A request that is red in the logger never started.

Safari's tracker prevention can drop or partition without an extension. Brave shields do the same. Firefox ETP has a standard and a strict mode. QA with each, plus one clean Chrome profile. A pixel that only fires on a developer laptop is often a machine without the list. The URL can still be valid when it does leave. Pixellint will pass a well-formed /tr that the blocker never sent.

EasyList hosts you will hit

www.facebook.com/tr, connect.facebook.net, www.google-analytics.com/g/collect, www.googletagmanager.com/gtm.js, googleadservices.com/pagead/conversion, ads.tiktok.com, analytics.tiktok.com, px.ads.linkedin.com, ct.pinterest.com, ad.doubleclick.net. The lists are public. If your pixel uses those hosts, a slice of users will never send the request. That slice is not randomly distributed; it correlates with desktop, with certain geos, and with engineering teams.

First-party aliases that still resolve to those hosts get listed eventually. CNAME cloaking was the 2019 dodge. Safari classified it. Lists added the CNAMEs. Do not play whack-a-mole with a new subdomain each quarter. Assume the vendor host is blocked for a material percent of browsers, and design the server path.

GTM fired versus request missing

GTM's preview marks a tag as fired when the container ran the tag's JS. That JS may call fbq('track','PageView'). fbq may queue. The network request to facebook.com/tr is a later step. A blocker that aborts that request still leaves preview green. Debug in Network, filtered to tr, g/collect, conversion/, collect?pid=. If the row is missing, the tag firing is a lie for measurement purposes.

ns.html and noscript images hit the same hosts. They do not bypass EasyList. A CSP that blocks the host looks identical in Network (no request) and different in the console (CSP error). Check both. Consent Mode denying ad_storage can also suppress or redact Google hits; that is not an ad blocker. Check the CMP state before you blame uBlock.

sGTM and CAPI actually recover signal

Server-side events from your backend (Meta CAPI, TikTok Events API, Google Ads upload) never touch the blocked host in the browser. Server-side GTM on a first-party collector (sgtm.example.com) moves the browser hit to a host the lists may not have tagged yet, then the server forwards to Google. That is an arms race, not a right. Disclose it. Do not cloak in a way that hides the destination from the user.

sGTM is not CNAME cloaking. You run a container on infrastructure you control. Safari's CNAME classification targeted DNS aliases to vendor hosts. A reverse proxy you understand, with a certificate you control, that POSTs to graph.facebook.com from the server, is the CAPI shape. The browser may still send a first-party collect to you. The vendor cookie on facebook.com is still third-party and still dying.

Consent tools are not blockers

A CMP that only hides the banner still lets the tag fire. A CMP that injects a blocking stub until choice is real will look like an ad blocker in the Network tab. Check whether the request is blocked by the extension or never queued by your consent wrapper. GTM Consent Overview tells you which tags waited. uBlock's logger tells you which hosts were aborted. They are different columns.

Do not disable the CMP to prove the pixel works and then ship that profile as production. Prove it with grant, deny, and grant-after-deny, with and without the extension. A Meta pixel that fires before consent in the EEA is a policy problem even if EasyList would have blocked it anyway. Two reasons not to send is still not a send.

Check the artifact

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