GPC Not Working on Your Site? Debug It in Order of Likelihood

A Global Privacy Control failure looks like a mystery for about an hour, and then it turns out to be one of seven things. The signal only travels two ways, as a request header and as a property on navigator, so there are a limited number of places it can go missing in transit, and a limited number of places a site can receive it correctly and still do the wrong thing afterwards.

What follows is that list, ordered by how often each cause turns out to be the answer. The order is the useful part. Worked top to bottom, the cheap checks run first and the expensive ones run only after the cheap ones are ruled out. Start at the browser and finish at the status display.

First, separate the two failures

"GPC is not working" describes two bugs with almost no overlap in their causes. In the first, the signal never reaches the site: nothing arrives, so nothing can react to it. In the second, the signal arrives intact and the site does not change its behaviour. Those are separate investigations, and running them at the same time is how an afternoon disappears.

One check separates them. Open DevTools, switch to the Network tab, reload the page, and click the document request at the top of the list. If Sec-GPC: 1 appears in the request headers, the signal left the browser. Then open the console and evaluate navigator.globalPrivacyControl. If it returns true, the signal is also visible to every script on the page. Both present means the browser is doing its job and the bug lives between the edge and the tags. Either one missing means the problem is in transport, and causes 1 and 2 cover it. Everything from cause 3 down assumes the signal arrived.

Cause 1: the browser is not sending it

This is the single most common answer, and it is the reason to confirm the signal at the browser before touching anything on the site. GPC is browser state, and browser state drifts. An extension gets disabled by an update. A second profile never had the extension installed. A private window runs with extensions off. A toggle resets after a settings migration. The test happens in a different browser than the one that was configured.

The confirmation is the DevTools check above, or a visit to globalprivacycontrol.org, which reads the signal and reports back. Two details are worth carrying forward. An absent header is not consent: it means the browser expressed nothing, and a site should treat it as no stated preference rather than as permission. And the header and the JavaScript property are separate paths, so confirming one does not confirm the other. A site that reads only the path you did not check will look broken for reasons that have nothing to do with the site.

Cause 2: a cache or CDN is stripping or ignoring the header

The second most common cause sits between the browser and the origin, and it comes in two distinct shapes. In the first, something in front of the application removes the header: a proxy, a WAF, or an edge rule that forwards an allowlist of headers written before anyone had heard of GPC. Origin logs settle this in one request. If the browser sent the header and the origin never logged it, the hop in between is the culprit.

The second shape is worse, because the header survives and the answer is still wrong. If the cache key does not include the header, one HTML variant serves both populations. Whichever version was cached first is what everyone receives, so a visitor sending GPC can be handed a page rendered for a visitor who was not, complete with the tags that were supposed to be suppressed. The tell is intermittency that tracks cache expiry rather than anything in the code. Two fixes exist: vary the cached response on the header and accept the extra cache entries, or stop making the decision at render time and make it in the browser from the JavaScript property instead.

Cause 3: the tag manager fires before consent resolves

From here down the signal is arriving and the question is what happens next. Cause 3 is a race, not a rule error, which is what makes it easy to misdiagnose: the configuration reads correctly when you inspect it, because the configuration is correct. It is simply not in force yet at the moment the tags fire.

A container that fires advertising tags on an initial page-view trigger will fire them before the consent layer has read the signal and resolved a decision. The request goes out, and nothing that happens afterwards recalls it. The tell is inconsistency: the page passes on a warm cache and fails on a cold one, or passes on a fast connection and fails on a slow one, because those change the order in which the container and the consent layer initialize. The fix is to make denial the default state and gate every affected trigger on an explicit consent-resolved event, so the ordering stops being something the network decides.

Cause 4: the consent rules map GPC to the wrong categories

Here the signal is read on time and routed to a category set that does not contain the tags that need to stop. The rule exists, it runs, and it suppresses a group of tags that were never the problem.

The usual shape is a mapping mismatch. GPC is wired to a category such as targeted advertising, while the pixel that keeps firing is filed under analytics, or under a strictly necessary category that is exempt from suppression by design. The diagnostic is a partial block: some third-party requests stop when the signal is present and others carry on. A clean pass or a total fail points elsewhere, but a split result is nearly always category assignment. Work it by listing the requests that still fire, looking up the category each of those tags is filed under, and comparing that against what your organization has documented as sale or sharing. Advertising and marketing tags are the clear case. Analytics needs a written position rather than a default, because the answer depends on where the data goes and who else gets to use it.

Cause 5: tags that never went through the consent layer

This one is a coverage gap rather than a rules problem, and it explains how a correct configuration can sit next to a firing tracker. A consent layer enforces decisions on the tags that route through it. A tag that never routed through it is not governed by those decisions, however carefully the rules were written.

Three sources account for most of them. Hardcoded pixels live directly in a theme file or a page template, usually added during a campaign and never migrated into the container. Embedded widgets bring their own tracking along with the feature someone wanted: chat launchers, review badges, map embeds, social share buttons, video players. Server-side and platform-level integrations fire from the back end, where a front-end consent decision never reaches them at all. Find them by diffing the network requests a page actually makes against the inventory of tags the consent layer manages. Anything in the first list and absent from the second is outside the coverage boundary. The fix is either to route it through the consent layer or to gate it in the template on the signal directly.

Cause 6: a stored preference is overriding the signal

A returning visitor may carry a business-specific setting from an earlier visit, stored in a cookie or against an account. Some implementations treat that stored value as authoritative and skip the signal whenever one exists, on the reasoning that an explicit past choice should outrank a browser-level default.

The regulation reverses that precedence. Under § 7025(c)(3), when a stored business-specific setting conflicts with a fresh GPC opt-out, the opt-out is what has to be honored. A business may notify the consumer of the conflict and offer a path to consent again, but it cannot quietly keep the old setting and let the new signal pass. The symptom is a split that depends on visitor history: a clean session passes and a session carrying prior state fails. Reproducing it requires a profile that has already set a site-specific preference, which is why it survives testing that only ever uses a fresh window.

Cause 7: it works, and nothing on the page says so

The last cause tends to surprise engineering teams, because by every technical measure the site is behaving. The trackers stop. The network trace is clean. The header is read and acted on. The finding is that the page never says it happened.

§ 7025(c)(6) requires a business to display whether it processed the signal. An opt-out status message, or a preference control reflecting the opted-out state, satisfies that. Silence does not, even when the suppression is flawless. What makes it awkward is that a business on the frictionless path may not answer with an interstitial: no pop-up, banner, animation, sound, or video in response to the signal, with narrow exceptions for showing opt-out status and linking to a privacy settings page. The display has to be quiet and persistent rather than loud and momentary, which is why it is the piece most often missing from an implementation that otherwise passes.

Getting an outside read

Debugging from inside the same browser profile that broke is how a fix gets declared twice. A free GPC check loads the URL from a clean session and reports what fired. That removes the variables a local test cannot hold still: the extension state and stored preferences of the profile doing the testing, and the cached artifacts that profile has already collected.

An outside read is also the only practical way to catch the causes that hide from the browser you are working in. A cache serving the wrong variant is invisible to a session that warmed that cache itself. A tag outside the consent layer looks identical to a tag inside it until you compare live requests against an inventory. The check answers the GPC question specifically, so treat the result as a verdict on the signal rather than as a survey of everything else a privacy review covers.

Once it passes, keeping it passing

A GPC implementation is not a launch task with a completion date. It is a property of a site that changes every time the site changes. A theme update reintroduces a hardcoded pixel. A new vendor arrives with an embedded widget. A campaign drops a tag straight into a template because the container review would have taken a week. A caching rule is rewritten for performance and quietly drops the header from the key. None of those touch the consent configuration, and every one of them can turn a passing site into a failing one without a line of the privacy code changing.

That is the argument for re-checking on a schedule, and for putting the checks next to the changes that break them: after a theme deploy, after a new vendor script lands, after an edge reconfiguration. If the diagnosis above points at the build rather than the browser, read the implementation itself, stack by stack. If it points at transport, and the open question is what should be arriving and in what form, read what the signal looks like on the wire.

Check this on your own site

Our free GPC checker loads one URL with the Global Privacy Control signal set and reports whether third-party trackers still fire.

Run a free GPC check