release

BareProxy Adds Cookie Consent That Holds Back Tracking Cookies Until a Visitor Says Yes

BareProxy 0.15.0 is out with its thirteenth plugin, cookie consent. It’s the fourth release today. The one before it, Markdown serving, came out earlier in the day.

Under the EU’s rules a site needs consent before it sets cookies it doesn’t need to work, such as the ones analytics and ad tools use. Most sites ask through a consent platform, a script loaded on every page. It adds weight, and it only protects visitors if the site’s other scripts actually wait for the answer.

A proxy is in a better spot for this. Every cookie a site’s app sets passes through it in a Set-Cookie header, and so does every page on its way out. So BareProxy can enforce the visitor’s choice as well as ask for it.

The plugin is consent.wasm on the releases page. Its page on this site has the details, and its manual is in the code repository.

What Visitors See

A visitor who hasn’t chosen yet gets a small banner at the foot of each page, with Accept all, Reject all and Choose. Accept and reject look alike and sit side by side, so neither one is the easy button. Choose opens a settings page with a box for each kind of cookie. A link to that page in the site’s footer lets a visitor change their mind later.

The buttons are plain links and the settings page is a plain form. There’s no JavaScript in any of it, so it all works with scripts turned off.

A browser that sends Global Privacy Control counts as having said no, and gets no banner at all.

What Waits for a Yes

Until a category is accepted, its cookies are taken out of every response, so they never reach the browser. They’re taken out of the requests the app gets as well, in case the browser kept one from before.

That covers the cookies the app sets. Most analytics and ad tools set theirs from JavaScript in the browser, where no proxy can see them. So the plugin keeps those scripts out of the page until there’s consent, picked by the address they load from. It knows Google Analytics, Meta’s pixel and the other common ones without any config, and a site can add its own. A script kept out gets type="text/plain", so the browser doesn’t run it. Iframes work the same way, so a YouTube embed named in the config waits for a yes too.

Taking consent back deletes that category’s cookies in the browser, for the site and for the domains above it, where trackers often set them. The app learns the choice too, from an X-Consent header on each request, for the few things only it can handle.

Each request’s record says what the plugin did:

showed the banner: no choice yet; kept 2 scripts out until consent: googletagmanager.com (analytics), connect.facebook.net (marketing)
held back _ga (analytics) and _fbp (marketing) until consent
consent: accepted analytics; withdrew marketing

status adds it up: banners shown, choices made, consent taken back, cookies held and scripts kept out. That’s the site’s own record of consent, and nothing is sent anywhere.

Tested in Chromium

Visitors click this banner, so it was tested in a real browser. Headless Chromium loaded a page with a tracking script on it. On the first visit the banner showed and the tracker stayed quiet, with no cookies set. After Accept all the browser was back on the page, the tracker ran and its _ga cookie was there. Unticking the box on the settings page stopped the tracker and deleted the cookie. With JavaScript off, Reject all still worked. With Global Privacy Control there was no banner.

That test caught a bug the server tests had missed. After a visitor took consent back, the browser could ask whether its kept copy of the page had changed, get “not modified” and show the old copy, tracker and all. Now a page the plugin may change is always fetched from the app in full, and a changed page goes out marked private, with nothing a kept copy can be checked against.

A Change in BareProxy Itself

The plugin needed a change in the core too. Plenty of apps send pages in chunks, with no length up front. Go’s proxy flushes those after every write, and BareProxy used to take that as a sign of streaming and let the body past its plugins, so exactly those pages would have gone out without a banner. From 0.15.0 a body a plugin asked to see is held until it’s complete. A real stream of events still goes straight through.

What It Can’t Do

A proxy sees the cookies that cross it, and no others. One that a script sets in the browser, or that a third party sets on its own domain, never crosses it; for those the plugin can only keep the script out of the page. A site with many third-party tags will still want a look from someone who knows them. Pages another plugin answers itself, such as Markdown pages, get no banner yet, though their cookies still wait. None of this is legal advice.

Next

Plugin 14 is uptime checks and status pages: checks on a timer and a public status page with 90 days of history, served by BareProxy itself. OpenTelemetry export comes after it. The full order is on the roadmap.