Skip to content

perf: stop prefetching every link in the viewport - #17

Merged
tiXor-code merged 1 commit into
mainfrom
perf/link-prefetch-off
Sep 7, 2026
Merged

tiXor-code merged 1 commit into
mainfrom
perf/link-prefetch-off

Conversation

@tiXor-code

Copy link
Copy Markdown
Owner

Why

faraapacalda.ro is 87% of the Vercel account's edge requests (644k of 740k over 31 days) and has pushed the Hobby plan past two allowances:

Metric Usage Limit
ISR Reads 1.4M 1M
Fast Origin Transfer 10.43 GB 10 GB
Edge Requests 843K 1M

On Hobby this does not generate a bill, it pauses the project. That would take the site down in the middle of the September hot-water season.

Measured cause

One homepage visit fired 35 ?_rsc= prefetch requests; a street page fired 24. Several are the same URL repeated under different _rsc cache-buster tokens:

/metodologie?_rsc=5CB68i4pnAekjehf
/metodologie?_rsc=7h4NYy5eoyMcNlUN
/metodologie?_rsc=yIp22O3xgPPxC28X   ... 5 variants in one page load

Each distinct RSC payload returns x-vercel-cache: PRERENDER, i.e. a read out of the prerender store, and they are large: 70 KB for a street page, 30 KB for /metodologie. That is roughly 1 MB of origin traffic per page view, spent fetching pages nobody asked for.

The change

Every route here is static, and visitors arrive from search and usually read one page, so prefetching the whole viewport buys very little. All links now route through components/Link.tsx, which defaults prefetch={false}. In the App Router that disables prefetch on viewport entry and on hover; navigation still fetches on click.

Diff is one line per file (the import swap) plus the wrapper and a test.

Measured after

  • Homepage origin requests 49 → 15
  • Zero ?_rsc= on load, on the homepage and on street pages
  • Exactly one _rsc per click, for the URL actually clicked (the navigation fetch itself)

Verification

  • npm test 125 passed (3 new)
  • npx tsc --noEmit clean
  • npx playwright test 34/34 passed (with Playwright building its own server, so NEXT_PUBLIC_APP_POLL_DELAY_MS is inlined correctly)
  • Headless browser check at 1440x900 and 390x844 on /, /strada/drm-taberei, /clasament: renders unchanged, 0 console messages, no failed first-party requests
  • Client-side navigation confirmed still soft: one document load across three URL changes, Back works from the router cache

Regression guard

test/link-prefetch.test.ts fails if any component imports next/link directly, so a newly added link cannot silently bring the storm back.

Not addressed here

This only removes the real-user half of the traffic. Roughly 60% of the site's edge requests are non-JS clients (crawlers) hitting HTML directly, which needs a CDN in front. Tracked separately.

faraapacalda.ro was 87% of the Vercel account's edge requests over the last
31 days (644k of 740k) and pushed the Hobby plan past its ISR-read and
fast-origin-transfer allowances, which pauses the project rather than billing
for it.

Measured cause: one homepage visit fired 35 `?_rsc=` prefetch requests and a
street page fired 24, several of them the same URL repeated under different
`_rsc` cache-buster tokens. Each distinct RSC payload comes back
`x-vercel-cache: PRERENDER`, i.e. a read out of the prerender store, and they
are not small - 70 KB for a street page, 30 KB for /metodologie. Roughly 1 MB
of origin traffic per page view to speculatively fetch pages nobody asked for.

Every route here is static and visitors arrive from search and usually read one
page, so prefetching the whole viewport buys very little. Route all links
through components/Link.tsx, which defaults prefetch={false}; in the App Router
that disables prefetch on viewport entry and on hover, while navigation still
fetches on click.

Measured after: homepage origin requests 49 -> 15, zero `?_rsc=` on load.

test/link-prefetch.test.ts fails if any component imports next/link directly,
so a new link cannot silently reintroduce the storm.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
termo-site Ready Ready Preview Sep 6, 2026 8:19pm UTC

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verified statically: every prior next/link import is now routed through components/Link.tsx (grep confirms no stray direct imports remain), the wrapper correctly defaults prefetch=false while still allowing per-call opt-in, and the new regression test's logic holds up (scans app/components/lib, matches wrapper behavior). Diff is a minimal, mechanical import swap + wrapper + guard test, matching the PR description exactly. No security-relevant surface touched (no auth/secrets/input-handling/dependency changes).

@tiXor-code
tiXor-code merged commit 8e5b8aa into main Sep 7, 2026
3 checks passed
@tiXor-code
tiXor-code deleted the perf/link-prefetch-off branch September 7, 2026 20:18
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