Skip to content

perf(edge): collapse CDN cache fragmentation on public GET routes#2566

Open
JohnPhamous wants to merge 1 commit intokoala73:mainfrom
JohnPhamous:perf/edge-cdn-cache-fragmentation
Open

perf(edge): collapse CDN cache fragmentation on public GET routes#2566
JohnPhamous wants to merge 1 commit intokoala73:mainfrom
JohnPhamous:perf/edge-cdn-cache-fragmentation

Conversation

@JohnPhamous
Copy link
Copy Markdown
Contributor

@JohnPhamous JohnPhamous commented Mar 30, 2026

  • ACAO: * on CDN-cached non-premium GET 200s: eliminates per-origin CDN cache variants (5+ variants per URL → 1). Premium routes (analyze-stock, backtest-stock, etc.) keep per-origin CORS to prevent cache-level auth bypass.
  • Cache middleware bot 403 responses: adds Cache-Control: public, max-age=86400 to bot/short-UA 403s. Vercel CDN does not cache 403, but Cloudflare and browsers do.
  • getPublicCorsHeaders() added to server/cors.ts (mirrors existing api/_cors.js precedent).

Expected impact

Cost

  • ~15-25% reduction in function invocations, edge requests, and origin transfer if Vary was fragmenting the CDN cache (one cache entry instead of 5+ per-origin variants)
  • Bot 403 caching saves repeated function invocations from persistent crawlers

Performance

  • If CDN hit rate improves, TTFB drops from ~200ms (function execution) to ~2-8ms (edge cache hit)

- ACAO: * on CDN-cached non-premium GET 200s to eliminate per-origin cache variants
- Premium routes (analyze-stock, backtest-stock, etc.) keep per-origin CORS
- Cache middleware bot 403 responses (CF/browser cache benefit)
- Add getPublicCorsHeaders() to server/cors.ts
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 30, 2026

@JohnPhamous is attempting to deploy a commit to the Elie Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions bot added the trust:safe Brin: contributor trust score safe label Mar 30, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 30, 2026

Greptile Summary

This PR collapses CDN cache fragmentation on public GET routes by introducing getPublicCorsHeaders() (returning ACAO: * without Vary: Origin) and applying it in the gateway for non-premium CDN-cached responses. It also caches bot-block 403 responses in the Edge Middleware and ships three new test files. The core caching optimization is sound, but there are two issues worth addressing before merge.

  • New getPublicCorsHeaders() helper (server/cors.ts) is cleanly separated from the per-origin variant; logic and interface are correct.
  • Gateway change (server/gateway.ts): for non-premium routes with a CDN cache, ACAO: * replaces the per-origin header and Vary is deleted. This is intentional but deliberately removes the isDisallowedOrigin() enforcement at the CDN cache layer — once Vercel CDN has a cached response, any origin (including disallowed ones) can read it via the browser. The PR would benefit from a comment acknowledging this trade-off so future maintainers don't place sensitive-but-non-premium data behind the assumption that the origin guard still protects it at cache time.
  • Middleware bot 403 (middleware.ts): Cache-Control: public, max-age=86400 is added to the 403 error response. Using public (rather than no-store or private) on a security-gate response is risky — if Cloudflare (confirmed in the codebase as sitting in front of api.worldmonitor.app) has any cache rule that respects public for 4xx responses, a bot hit could cause a cached 403 to be served to legitimate users for 24 hours from the same URL, since there is no Vary: User-Agent.
  • New tests (tests/cors-public-headers.test.mts, tests/gateway-cdn-cors.test.mts, tests/middleware-bot-cache.test.mts) use readFileSync + regex on TypeScript source text rather than importing and invoking actual functions. This pattern is fragile to code formatting changes and does not exercise runtime behavior; the existing api/_cors.test.mjs (also updated in this PR) demonstrates the preferred import-and-call pattern.

Confidence Score: 4/5

  • Mostly safe to merge, but the public directive on bot-block 403 responses in middleware carries a real (if CDN-config-dependent) risk of serving cached 403s to legitimate users.
  • One P1 finding in middleware.ts — Cache-Control: public on a security 403 response without Vary: User-Agent can cause CDN layers with custom cache rules (e.g. Cloudflare page rules) to cache and replay the 403 to legitimate users for up to 24 hours. The gateway change is intentional and correctly guarded for premium routes. The test quality concerns are P2 style issues.
  • middleware.ts — the public, max-age=86400 header on bot-block 403 responses. server/gateway.ts — the origin-bypass behavior change warrants a clarifying comment.

Important Files Changed

Filename Overview
server/cors.ts Adds getPublicCorsHeaders() returning ACAO: * without Vary: Origin; cleanly isolated from the existing per-origin getCorsHeaders.
server/gateway.ts Applies ACAO: * + deletes Vary for non-premium CDN-cached GET 200s; intentionally bypasses the isDisallowedOrigin() guard at the CDN layer — the behavioral change should be documented for future maintainers.
middleware.ts Adds Cache-Control: public, max-age=86400 to bot/short-UA 403 responses; using public on a security gate response is risky if any CDN layer (e.g. Cloudflare with a custom cache rule) caches 4xx without Vary: User-Agent.
api/_cors.test.mjs Adds a correct, runtime-level test for getPublicCorsHeaders; asserts ACAO: * and absence of Vary.
tests/cors-public-headers.test.mts Tests use readFileSync + regex on TypeScript source text rather than importing the function — fragile against reformatting and doesn't exercise runtime behavior.
tests/gateway-cdn-cors.test.mts Same source-text-parsing anti-pattern as cors-public-headers.test.mts; assertions check source code string patterns, not gateway runtime behavior.
tests/middleware-bot-cache.test.mts Source-text-parsing test that verifies bot/short-UA blocks include Cache-Control; same fragility concern as the other new test files, but the assertions here are less structurally sensitive.

Sequence Diagram

sequenceDiagram
    participant C as Browser (any origin)
    participant CF as Cloudflare
    participant VCDN as Vercel CDN
    participant MW as Edge Middleware
    participant GW as Edge Function (gateway)

    Note over C,GW: First request — allowed origin, non-premium route (cache cold)
    C->>CF: GET /api/market/v1/list-market-quotes (Origin: worldmonitor.app)
    CF->>VCDN: forward (no CF cache)
    VCDN->>MW: cache miss → run middleware
    MW->>GW: UA passes bot check → forward
    GW->>GW: isDisallowedOrigin() → false\nisAllowedOrigin() → true\ncdnCache set
    GW-->>VCDN: 200 + CDN-Cache-Control + ACAO:* + no Vary
    VCDN->>VCDN: store response (keyed by URL only)
    VCDN-->>C: 200 ACAO:*

    Note over C,GW: Subsequent request — disallowed origin (cache warm)
    C->>CF: GET /api/market/v1/list-market-quotes (Origin: evil.example.com)
    CF->>VCDN: forward
    VCDN->>VCDN: cache HIT (no Vary, URL matches)
    VCDN-->>C: 200 ACAO:* (isDisallowedOrigin never runs)
    Note over C: Browser allows cross-origin read\nbecause ACAO: *

    Note over C,GW: Bot UA request — middleware blocks
    C->>CF: GET /api/... (Bot UA)
    CF->>VCDN: forward
    VCDN->>MW: run middleware
    MW-->>C: 403 Cache-Control: public, max-age=86400
    Note over CF: ⚠️ If CF has a custom cache rule\nfor 4xx: caches 403 by URL\nwithout Vary: User-Agent
Loading

Reviews (1): Last reviewed commit: "perf(edge): collapse CDN cache fragmenta..." | Re-trigger Greptile

Comment on lines 122 to 126
if (BOT_UA.test(ua)) {
return new Response('{"error":"Forbidden"}', {
status: 403,
headers: { 'Content-Type': 'application/json' },
headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=86400' },
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 public on bot-block 403 can be served as a CDN cache hit to legitimate users

The middleware returns Cache-Control: public, max-age=86400 on both bot-UA 403 responses (line 124) and short-UA 403 responses (line 132). Per Vercel's execution model, CDN cache checks happen before middleware runs — so once any CDN layer in the request path caches a 403 response keyed only on the URL (no Vary: User-Agent), legitimate users requesting that same URL will receive the cached 403 without middleware ever executing.

Vercel CDN itself only stores responses with s-maxage or CDN-Cache-Control, so Vercel's own cache is safe here. However, the code comments confirm Cloudflare sits in front of api.worldmonitor.app — Cloudflare respects Cache-Control: public for its cache rules, and while it does not cache 4xx by default, a custom Page Rule or Cache Rule (present in many production CF setups) could enable it. Any such CF config would cause all requests to a URL hit by a bot to return 403 to real users for 24 hours.

public is the wrong directive for a security-gate error response; the intent (telling bots to back off) is better served by no-store or omitting Cache-Control entirely:

Suggested change
if (BOT_UA.test(ua)) {
return new Response('{"error":"Forbidden"}', {
status: 403,
headers: { 'Content-Type': 'application/json' },
headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=86400' },
});
headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' },

The same fix applies at line 132–134.

Comment on lines +378 to +382
if (!PREMIUM_RPC_PATHS.has(pathname)) {
const pub = getPublicCorsHeaders();
for (const [k, v] of Object.entries(pub)) mergedHeaders.set(k, v);
mergedHeaders.delete('Vary');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 ACAO: * on CDN-cached responses bypasses isDisallowedOrigin() for browser clients

The code removed this warning from the previous version:

ACAO: * with no Vary would collapse all origins into one cache entry, bypassing isDisallowedOrigin() for cache hits — Vercel CDN serves s-maxage responses without re-invoking the function, so a disallowed origin could read a cached ACAO: * response.

That observation is still true after this PR. The flow is now:

  1. Request from allowed origin hits a non-premium route → edge function runs → isDisallowedOrigin() passes → response is cached by Vercel CDN with ACAO: *, Vary deleted.
  2. Request from a disallowed origin (e.g. https://evil.example.com) for the same URL → Vercel CDN cache hit → edge function never runs → isDisallowedOrigin() never executes → browser receives ACAO: * response and the cross-origin read succeeds.

This is intentional for "public" data routes, and the premium guard is in place, so this isn't a catastrophic auth bypass. But it is a meaningful change in the access-control surface: previously, disallowed origins could not read any route via the browser (they got a 403 from the function). Now they can read all non-premium CDN-cached routes.

If the intent is that non-premium routes should be globally readable, this is fine — but it's worth explicitly documenting that isDisallowedOrigin() is no longer enforced at the CDN cache layer for public routes, so future maintainers don't add sensitive-but-non-premium data under the assumption that the origin guard protects it.

Comment on lines +7 to +29
const __dirname = dirname(fileURLToPath(import.meta.url));
const corsSrc = readFileSync(resolve(__dirname, '..', 'server', 'cors.ts'), 'utf-8');

describe('getPublicCorsHeaders', () => {
it('returns ACAO: * without Vary header', () => {
// Verify the function body contains ACAO: * and does NOT include Vary
const fnMatch = corsSrc.match(/export function getPublicCorsHeaders[\s\S]*?^}/m);
assert.ok(fnMatch, 'getPublicCorsHeaders function not found in server/cors.ts');
const fnBody = fnMatch![0];
assert.match(fnBody, /'Access-Control-Allow-Origin':\s*'\*'/, 'Should set ACAO: *');
assert.doesNotMatch(fnBody, /Vary/, 'Should NOT include Vary header');
});

it('includes same Allow-Methods as getCorsHeaders', () => {
const pubMethods = corsSrc.match(/getPublicCorsHeaders[\s\S]*?Allow-Methods':\s*'([^']+)'/);
const perOriginMethods = corsSrc.match(/getCorsHeaders[\s\S]*?Allow-Methods':\s*'([^']+)'/);
assert.ok(pubMethods && perOriginMethods, 'Could not extract Allow-Methods from both functions');
assert.equal(pubMethods![1], perOriginMethods![1], 'Allow-Methods should match between public and per-origin');
});

it('includes same Allow-Headers as getCorsHeaders', () => {
const pubHeaders = corsSrc.match(/getPublicCorsHeaders[\s\S]*?Allow-Headers':\s*'([^']+)'/);
const perOriginHeaders = corsSrc.match(/getCorsHeaders[\s\S]*?Allow-Headers':\s*'([^']+)'/);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Tests inspect source text rather than exercising runtime behavior

Both tests/cors-public-headers.test.mts and tests/gateway-cdn-cors.test.mts use readFileSync + regex over the TypeScript source files instead of importing and calling the actual functions. This approach:

  • Will false-pass if the source text matches but the compiled output behaves differently (e.g., an environment variable override, dead branch, or build-time transformation changes the effective value).
  • Will false-fail if anyone switches quote style ('"), reformats, or renames a local variable, even though the runtime behavior is unchanged.
  • Doesn't catch mismatches between cors.ts and gateway.ts at integration level — only surface-level text patterns.

The api/_cors.test.mjs (also changed in this PR) shows the correct pattern: import the function, call it, and assert on the returned value. Bringing the new tests in line with that approach would give much stronger correctness guarantees:

// tests/cors-public-headers.test.mts — preferred approach
import { getPublicCorsHeaders, getCorsHeaders } from '../server/cors.ts';
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';

describe('getPublicCorsHeaders', () => {
  it('returns ACAO: * without Vary header', () => {
    const h = getPublicCorsHeaders();
    assert.equal(h['Access-Control-Allow-Origin'], '*');
    assert.equal(h['Vary'], undefined);
  });
  // ...
});

The same applies to tests/gateway-cdn-cors.test.mts — the assertions there are checking for specific source text patterns (e.g. !PREMIUM_RPC_PATHS\.has\(pathname\)) that would be better covered by an integration test that exercises createDomainGateway end-to-end.

@koala73
Copy link
Copy Markdown
Owner

koala73 commented Mar 31, 2026

Thanks @JohnPhamous much appreciated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

trust:safe Brin: contributor trust score safe

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants