Skip to content

fix: checks follow redirects on one connection and send no cache headers - #48

Merged
kroqdotdev merged 1 commit into
mainfrom
fix/measured-like-a-browser
Oct 24, 2026
Merged

kroqdotdev merged 1 commit into
mainfrom
fix/measured-like-a-browser

Conversation

@kroqdotdev

Copy link
Copy Markdown
Owner

Two measurement fixes from the hosted edition's audit of 24 Oct 2026.

Redirects. fetch's own redirect handling cancels each hop's body, which closes its connection, so a redirect to the same host opened a second connection and paid the TCP and TLS handshakes again. A browser reuses the connection. The HTTP check now follows redirects by hand: it reads a redirect's body (up to 64 KB, at most 250 ms), waits one turn of the event loop so undici returns the socket to the pool, and sends the next hop over it. Browser rules are kept: credentials stay with their own origin, and a 303 (or a 301 or 302 after a POST) continues as a GET without the body. Measured on hosted's production prober for statoss.com/app (307 to /login): one TLS handshake instead of three, about 150 ms instead of about 215 ms.

Cache headers. cache: "no-store" made undici send cache-control: no-cache and pragma: no-cache on every check. Node's fetch keeps no cache, so the option did nothing else. Some caches obey those headers (Squid, Apache mod_cache, some application caches) and visitors never send them, so a site behind one was measured on its uncached page. A monitor that wants to reach the server behind the cache can send the header in its headers.

Tests: a same-host redirect uses one connection (with and without a body), a stalled redirect body does not hold the check, a POST continues as a GET after a 303, and no cache headers are sent. Both new connection and header tests fail on main. README and CHANGELOG updated.

Local: lint, format check, typecheck, 513 tests and the build pass.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 039c1c34-921b-494b-88f3-711a9b383b94
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

kroqdotdev added a commit that referenced this pull request Oct 24, 2026
…#49)

`pnpm audit --audit-level=high` in **checks** fails on main, so every
pull request is red, Dependabot's #47 and #48 included.

- **next 16.4.0 to 16.4.2** (and eslint-config-next):
GHSA-2464-fhr4-w9xw (critical, remote code execution through image
optimization when `sharp` is older than 0.35.5 on glibc),
GHSA-673q-mjm6-r5xr (critical, `use cache` returning typed arrays),
GHSA-p3m5-59mp-jv5r (high, Server Actions denial of service). Checked
against what runs: the published 0.5.0 image and the demo on statoss.com
both carry `sharp` 0.35.5, and the app has no `use cache` and no Server
Actions, so none of the three reaches a running copy. The bump is still
due.
- **vite 8.2.0 to 8.2.4** through an override in `pnpm-workspace.yaml`,
as the repo does for other transitive fixes: six advisories in its
development server, reached only through vitest.
- The rest of #47: react and react-dom 19.3.1, nodemailer 10.1.7,
@playwright/test 1.64.1, vitest 5.0.5.

`pnpm audit` reports no known vulnerabilities. Local: lint, format
check, typecheck, 509 tests and the build pass. CHANGELOG has a Security
entry.

Supersedes #47.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A redirect to the same host now goes over the same connection, as in a
browser: fetch's own redirects cancelled the hop's body, which closed
its connection, so every hop paid a new TCP and TLS handshake. The
check follows redirects by hand, reads a redirect's body (up to 64 KB,
250 ms) and lets the connection go back to the pool before the next hop.

cache: "no-store" is gone from the HTTP check. Node's fetch keeps no
cache, so it only added cache-control and pragma no-cache, which some
caches obey and visitors never send.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kroqdotdev
kroqdotdev force-pushed the fix/measured-like-a-browser branch from 56c4b2c to e86cd7a Compare October 24, 2026 17:48
@kroqdotdev
kroqdotdev merged commit a5d90e4 into main Oct 24, 2026
4 checks passed
@kroqdotdev
kroqdotdev deleted the fix/measured-like-a-browser branch October 24, 2026 17:51
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.

1 participant