Skip to content

don't proxy sensitive headers - #3272

Merged
huumn merged 2 commits into
masterfrom
plausible-header-filter
Oct 8, 2026
Merged

huumn merged 2 commits into
masterfrom
plausible-header-filter

Conversation

@huumn

@huumn huumn commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

withPlausibleProxy naively passes ALL headers to Plausible on rewrite (because nextjs does not allow header removals in next.config.js rewrite rules). This PR avoids this by doing the rewrite ourselves in proxy.js using a whitelist of headers that we send upstream.

We bind PlausibleProvider's init to PLAUSIBLE_EVENT_PATH which helps prevent all headers getting passed should the lib ever change that path.


Note

Medium Risk
Changes how analytics traffic is proxied and which headers leave the app; misconfiguration could break Plausible or still leak headers if paths drift from the matcher.

Overview
Moves Plausible analytics proxying out of next-plausible's withPlausibleProxy so upstream requests no longer forward every client header to plausible.io.

A new lib/plausible.js centralizes first-party paths (/js/script.js, /api/event) and their plausible.io targets. pages/_app.js points PlausibleProvider at those paths and sets init.endpoint to the event path. proxy.js rewrites those routes with a whitelist of request headers, sets host to strip the rest, and applies the same security headers as other responses; the matcher gains explicit entries for the two Plausible paths (kept in sync with the constants via comment). next.config.js drops the withPlausibleProxy wrapper while keeping transpilePackages: ['next-plausible'].

Reviewed by Cursor Bugbot for commit 40aec5e. Bugbot is set up for automated code reviews on this repo. Configure here.

@huumn
huumn force-pushed the plausible-header-filter branch from 7fe8157 to 98568a6 Compare October 2, 2026 23:56
@huumn

huumn commented Oct 3, 2026

Copy link
Copy Markdown
Member Author

Force push to pin PLAUSIBLE_EVENT_PATH and update to a whitelist-based header approach.

Comment thread proxy.js Outdated
Comment on lines +308 to +309
// Next requires a literal matcher. Tests keep this in sync with lib/plausible.
'/([aA][pP][iI]/[eE][vV][eE][nN][tT])',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have trouble understanding:

  • the "tests" comment here because how does this sync? Doesn't this in fact desync?
  • why PLAUSIBLE_SCRIPT_PATH does not match here
  • the point of the uppercase. Is /aPi/ in a path the same as /ApI/?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the "tests" comment here because how does this sync? Doesn't this in fact desync?

Sorry. That's referencing some bot tests and a bot comment.

why PLAUSIBLE_SCRIPT_PATH does not match here

It's already covered by the other matcher. PLAUSIBLE_EVENT_PATH needed its own matcher because the other matcher excludes /api paths.

the point of the uppercase. Is /aPi/ in a path the same as /ApI/?

Bots being bots. I'll see if I can use PLAUSIBLE_EVENT_PATH.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see if I can use PLAUSIBLE_EVENT_PATH.

We can't use constants in matchers, but I'll make it all lowercase and add a comment about needing to keep it in sync.

@huumn

huumn commented Oct 4, 2026

Copy link
Copy Markdown
Member Author

QA instructions

  1. start echo server node -e "require('http').createServer((q,s)=>{console.log('\n'+q.method,q.url);console.log(q.headers);q.resume();s.end('ok')}).listen(9999)"
  2. change plausible destinations to the echo server
    export const PLAUSIBLE_DESTINATIONS = {
      [PLAUSIBLE_SCRIPT_PATH]: 'http://host.docker.internal:9999/js/pa-test.js',
      [PLAUSIBLE_EVENT_PATH]: 'http://host.docker.internal:9999/api/event'
    }
  3. logged in, navigate seeing /js/pa-test.js getting hit on the echo server with sensitive headers stripped
  4. change PLAUSIBLE_SCRIPT_PATH to the actual script to test /api/event
    export const PLAUSIBLE_DESTINATIONS = {
      [PLAUSIBLE_SCRIPT_PATH]: 'https://plausible.io/js/pa-EScEhWlTi3E-sauvdFABb.js',
      [PLAUSIBLE_EVENT_PATH]: 'http://host.docker.internal:9999/api/event'
    }
  5. logged in, navigate around seeing /api/event getting hit on the echo server with sensitive headers stripped

To QA x-api-key and more getting stripped, follow (1) & (2), then curl:

H=(-H 'Cookie: __Secure-next-auth.session-token=SECRET1; multi_auth=SECRET2'
   -H 'Authorization: Bearer SECRET3' -H 'X-Api-Key: SECRET4'
   -H 'X-Stacker-News-Referrer: spoof' -H 'User-Agent: qa-test'
   -H 'X-Forwarded-For: 555.5.555.5')
curl -si "${H[@]}" http://localhost:3000/js/script.js
curl -si "${H[@]}" -X POST -H 'Content-Type: text/plain' -d '{"n":"pageview","u":"http://localhost:3000/"}' http://localhost:3000/api/event

@huumn
huumn merged commit 85e105c into master Oct 8, 2026
7 checks passed
@huumn
huumn deleted the plausible-header-filter branch October 8, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants