Performance
static.bio treats performance as a product constraint, not an implementation detail.
Public profiles are optimized for:
- small payloads
- fast server response
- predictable rendering (no client runtime)
This page defines what we measure, what we target, and how to verify it.
Performance goals
Public profile budgets (targets)
These are targets, not promises of physics across every geography and cache state:
- HTML payload: small and stable per profile
- CSS payload: ≤ 2 files (base + theme), cacheable and fingerprinted
- No JS: public pages ship 0
<script>tags - Fast TTFB: optimized for low-latency responses under normal load
Budgets are validated via automated checks (see Contract tests and Verify).
Performance snapshot
Methodology
- Harness
- All measurements use Playwright + Chromium (headless), Desktop profile, no throttling.
- Tests target production (
https://static.bio) to capture real-world behavior: edge routing, HTTP/2 connection pooling, and geographic latency.
- TTFB
- Definition: TTFB is computed from the browser's Navigation Timing API as:
responseStart - requestStartThis includes DNS, TCP, TLS, and server processing as observed by the browser. - Warm cache (same context / pooled connection)
- After warm-up navigations, run 5 batches × 10 concurrent navigations in the same browser context (50 samples).
- Purpose: measure TTFB under burst while preserving HTTP/2 connection reuse.
- Cold cache (new context per sample)
- For each run, create a fresh
browser.newContext(), navigate once, record TTFB, then close the context. - 20 samples, with 500ms between runs.
- Purpose: isolate the cost of new connections (DNS/TCP/TLS) without reuse.
- For each run, create a fresh
- Parallel cold (new contexts, concurrent)
- Run 5 batches × 10 concurrent navigations, each in a fresh context (50 samples).
- Purpose: measure contention under concurrent new connections.
- Note on correctness: Warm-cache measurement uses concurrency on a reused connection pool (not sequential requests on a single page) to avoid conflating "time passing" variance with connection reuse effects.
- Definition: TTFB is computed from the browser's Navigation Timing API as:
- Page spec
- Page transfer (cold / warm)
- Uses Resource Timing entries:
performance.getEntriesByType("resource") - Computes sum of
transferSizeacross resources. transferSizeincludes headers + compressed body, and becomes 0 for cache hits.- Cold = first visit; Warm = second visit with cache retained.
- Uses Resource Timing entries:
- Third-party requests
- Compares each resource's origin against the document origin (same-origin policy).
- Excludes the user-provided avatar URL so the metric reflects platform-controlled resources only (verifying the "same-origin" guarantee for static.bio assets).
- Page transfer (cold / warm)
- Measurement challenges (what we fixed)
- Node fetch vs browser TTFB: Node.js
fetch()does not reflect browser HTTP/2 pooling or proximity. We measure TTFB via Navigation Timing instead. - Load timing definition: "networkidle" introduces a fixed quiet-window delay; we use
waitUntil: "load"and load-event timing for end-to-end measurements. - FCP in headless: paint timing is unreliable in headless.
- Connection isolation for cold runs: cold scenarios close contexts after each measurement and introduce delays to reduce accidental reuse.
- Node fetch vs browser TTFB: Node.js
- Statistics
- Scenarios use 20–50 samples each.
- Percentiles are computed on sorted samples using index
floor(n * 0.95). - Display rounding avoids visual artifacts:
- ms values can be shown at 0–1 decimal precision as needed.
- KB values are displayed to 2 decimals; p95 display is rounded such that it cannot appear lower than avg due to rounding.
What "fast" means here
Performance is not only about raw time; it's about removing classes of work:
- No client-side runtime → no JS parse/execute
- No third-party resources → no slow external dependencies
- Deterministic renderer → stable output, cacheable primitives
- Fingerprinted CSS → aggressive reuse across profiles
The result is a page that becomes "free" on repeat visits: the browser cache holds base/theme CSS, and the HTML is minimal.
Metrics we care about
Server-side
- TTFB (time to first byte)
- p95 / p99 TTFB (tail latency matters)
- cache hit ratio (if you cache rendered HTML)
- response size (compressed)
Client-side
- bytes transferred (compressed)
- render blocking resources (should be CSS only)
- number of requests
- LCP/FCP in typical conditions (no JS makes these mostly a function of CSS and images)
Why we accept 2 CSS requests
Public profiles load:
base.<hash>.csstheme-<id>.<hash>.css
This is intentional:
- Those files are shared across many profiles.
- They are served with immutable caching headers.
- After the first visit, they typically cost 0 bytes (cache hit).
We optimize for cacheability and byte budget, not "single-response pages."
Export mode is the exception: it may inline CSS for portability.
Measurement methodology
1) Over-the-wire bytes
Measure compressed bytes from the server. Example:
- HTML bytes (compressed transfer) for a profile
- CSS bytes (compressed transfer) for base and a theme
2) Request audit
Verify that public pages:
- include no scripts
- load only same-origin resources
- load ≤ 2 stylesheets
3) Tail latency
Measure p95/p99 TTFB in production-like conditions, not only locally.
The renderer itself is cheap; tail latency is typically dominated by:
- data store latency
- cold starts / scaling events
- geography
- cache state
Practical optimizations (what we do and don't do)
We do
- Keep HTML structure stable
- Prefer CSS variables and theme deltas
- Fingerprint and immutable-cache CSS assets
- Avoid third-party assets
- Keep image usage minimal (avatar only, unless the user adds more later)
We avoid
- Client-side hydration
- third-party analytics scripts
- heavy font loading by default
- "hero section" layout complexity
Known tradeoffs
- Geography matters. A Toronto request and a Singapore request will not have the same TTFB.
- Cold caches happen. The first request after deployment or cache eviction is slower.
- Avatars can dominate LCP. If avatars are large or external, they can become the slowest resource.
- Per-profile CSS inlining doesn't scale. It reduces requests but destroys shared caching; we prefer shared assets.
Contract tests (performance-level)
These are invariants we can assert mechanically:
- Public HTML contains 0
<script>tags. - Public HTML loads ≤ 2 stylesheets (base + theme) in non-export environments.
- All stylesheet URLs are same-origin (prefer relative paths).
- Base/theme CSS filenames are fingerprinted and served with immutable caching headers.
- Total transferred bytes for HTML stay under a defined ceiling (set a CI threshold).
Exact commands and expected outputs live in Verify (curl cookbook).
How to verify
See: Verify (curl cookbook) for copy/paste commands that measure:
- bytes transferred
- number of stylesheet links
- presence of scripts
- response headers and caching behavior