Landing Pages at 100M Views: Next.js vs Astro vs Hugo vs Eleventy — Build Latency, TTFB, and True Cost per Million Visitors Benchmarked
At 100M monthly views, landing-page performance is less about framework demos and more about cache hit ratio, build invalidation, and egress economics. This benchmark-driven guide compares Next.js, Astro, Hugo, and Eleventy on build latency, TTFB, Core Web Vitals, and true cost per million visitors.
Photo by panumas nikhomkhai on PexelsAt 100M monthly views, a landing page is a distributed cache with a content workflow attached. The framework matters, but not in the way most comparisons imply: build speed affects release velocity, TTFB affects conversion and crawl budget, and cost per million is dominated by bytes, cache misses, and compute that should never have run.
Define the Workload Before You Benchmark
A meaningful static site generator benchmark must fix the workload. The reference workload in this article is 100M monthly HTML page views, 500 CMS-backed landing pages, 10 deploys per day, 2 MB compressed transfer per view, and a 99.5% edge cache hit ratio for anonymous HTML. Images are served from an image CDN, analytics is asynchronous, and personalization is client-side or edge-cached as explicit variants. No logged-in HTML is cached publicly.
That traffic level is about 38.6 average requests per second when spread across 30 days, but landing-page traffic is bursty. Plan for a 6x peak factor, roughly 230-300 RPS, and test cache-cold behavior separately from cache-warm behavior. At 99.5% hit ratio, 100M views produce 500,000 origin renders per month. At 95%, the same traffic produces 5M origin renders, a 10x increase in TTFB-sensitive work and compute spend.
Build Latency: Release Velocity, Not Hot-Path TTFB
Build latency is often misused in framework debates. At 100M views, a healthy edge cache means the build system is not in the request path. Build latency matters because it controls editorial cadence, cache invalidation windows, rollback speed, and how safely you can rebuild after a headless CMS change.
The following ranges are directional, not universal. They assume 5,000 static marketing pages on a 4 vCPU CI runner with external image URLs and no heavy data transforms. Always benchmark your own templates, data sources, and plugins.
| Framework | Cold build at 5k pages | Incremental path | Runtime model |
|---|---|---|---|
| Hugo | 5-30s | Full rebuild, very fast | Go binary, static output |
| Eleventy | 30-120s | Full rebuild plus incremental plugins | Node.js, static output |
| Astro | 90-300s | Static pages plus islands | Static HTML plus selective hydration |
| Next.js | 3-15 min | ISR, on-demand revalidation, static export | React server rendering plus static/ISR |
Hugo wins raw cold-build throughput for content-heavy sites. Eleventy offers flexible JavaScript data pipelines with predictable static output. Astro keeps pages static while allowing interactive islands. Next.js provides the richest React application model and incremental rendering, but its build and cache semantics require more operational discipline.
TTFB: Edge Hit Latency Beats Origin Render Latency
TTFB is a compound metric: DNS, TLS, edge lookup, cache status, origin fetch, rendering, and streaming. For anonymous landing pages, the biggest lever is not the framework. It is the cache hit ratio and cache header correctness. A static HTML object served from an edge POP can be 20-100 ms P50 in many regions. A server-rendered origin page is commonly 200-800 ms before cold starts, and p99 can be much worse.
Model TTFB as:
p50_ttfb ≈ edge_lookup + miss_ratio × origin_render_time
Lowering miss ratio from 5% to 0.5% reduces origin renders by 90% and removes tail latency from most users. Use s-maxage, stale-while-revalidate, and tag-based purge to absorb traffic spikes. Core Web Vitals still matter: TTFB influences LCP, while JavaScript and third-party tags usually decide INP. A landing page with excellent TTFB and a bloated tag manager can still lose conversion rate optimization gains.
Next.js: Fast When Static, Expensive When Accidentally Dynamic
Next.js can be an excellent 100M-view landing-page platform, but only if you keep routes static or ISR-cached and avoid per-request dynamic APIs. App Router caching is powerful and easy to invalidate correctly. It is also easy to invalidate accidentally with cookies(), headers(), searchParams, or uncached fetch calls.
import { revalidatePath } from 'next/cache';
export const revalidate = 3600;
type LandingPage = { slug: string; headline: string; heroUrl: string };
async function getLandingPage(slug: string): Promise<LandingPage> {
const res = await fetch(`https://cms.example.com/api/landing-pages/${slug}`, {
next: { revalidate: 3600, tags: [`landing:${slug}`] },
});
if (!res.ok) throw new Error(`CMS fetch failed: ${res.status}`);
return (await res.json()) as LandingPage;
}
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const page = await getLandingPage(slug);
return <main><h1>{page.headline}</h1></main>;
}
export async function POST(req: Request) {
const secret = req.headers.get('x-revalidate-secret');
if (!secret || secret !== process.env.REVALIDATE_SECRET) {
return Response.json({ error: 'unauthorized' }, { status: 401 });
}
const body = (await req.json()) as { slug?: string };
if (!body.slug) return Response.json({ error: 'slug required' }, { status: 400 });
revalidatePath(`/lp/${body.slug}`);
return Response.json({ revalidated: true });
}Use generateStaticParams for known landing-page slugs, and prefer tag-based revalidation over rebuilding the entire site for one CMS edit. Watch middleware: it runs on matching requests and can introduce latency and cost at 100M views. The common failure mode is a route that looks static in development but becomes dynamic in production because a cookie or header is read during render.
Astro: Static HTML With Islands Where Interaction Pays Rent
Astro is a strong fit for marketing sites because the default is static HTML. React, Vue, Svelte, or Solid components hydrate only where explicitly marked with a client directive. That keeps JavaScript budgets low and makes edge caching simple. The trade-off is that app-like personalization and authenticated flows are less natural than in Next.js.
---
export const prerender = true;
type LandingPage = { slug: string; headline: string; heroUrl: string };
export async function getStaticPaths() {
const res = await fetch('https://cms.example.com/api/landing-pages');
if (!res.ok) throw new Error(`CMS fetch failed: ${res.status}`);
const slugs = (await res.json()) as string[];
return slugs.map((slug) => ({ params: { slug } }));
}
const { slug } = Astro.params;
if (!slug) return Astro.redirect('/404');
const res = await fetch(`https://cms.example.com/api/landing-pages/${slug}`);
if (!res.ok) throw new Error(`CMS fetch failed: ${res.status}`);
const page = (await res.json()) as LandingPage;
---
<main>
<h1>{page.headline}</h1>
<img src={page.heroUrl} alt='' width='1200' height='630' loading='eager' fetchpriority='high' />
<NewsletterForm client:visible />
</main>Treat getStaticPaths and CMS fetches as build inputs. Cache CMS responses at the CI layer, rate-limit webhooks, and avoid injecting request-time secrets into static pages. Astro's island model is excellent for landing page performance, but a single eagerly hydrated analytics or experimentation widget can erase much of the advantage.
Hugo and Eleventy: Operational Simplicity at Scale
Hugo is the throughput leader for large content sets. It compiles to a static site with no client runtime by default, so origin infrastructure can be a bucket plus CDN. Eleventy is similarly static but embraces JavaScript data files and plugins, which is valuable for teams that need custom integrations without adopting a full framework runtime.
Both stacks benefit from aggressive edge caching. A production static-host header policy might look like this:
/lp/*
Cache-Control: public, max-age=0, s-maxage=86400, stale-while-revalidate=604800
/assets/*
Cache-Control: public, max-age=31536000, immutableKeep HTML revalidation short enough for campaign changes, and fingerprint assets for immutable caching. If you use a headless CMS webhook, debounce it. A CMS import that triggers 500 deploys can cost more in CI and cache churn than the runtime savings from static generation.
True Cost per Million Visitors: Use a Workload Model
Vendor pricing pages rarely answer the right question. Model cost per million views with the variables that dominate at scale: egress bytes, edge requests, origin renders, image transforms, CI builds, and observability. This TypeScript model is intentionally auditable and rejects invalid inputs.
type Workload = {
monthlyViews: number;
avgBytesPerView: number;
cacheHitRatio: number;
cdnEgressUsdPerGb: number;
edgeRequestUsdPerMillion: number;
originRenderUsdPerMillion: number;
imageTransformUsdPerThousand: number;
imageTransformsPerMillionViews: number;
monthlyCiBuildUsd: number;
observabilityUsd: number;
};
function costPerMillionViews(w: Workload): { totalMonthlyUsd: number; cpmUsd: number; egressUsd: number; originRenders: number } {
if (w.monthlyViews <= 0) throw new Error('monthlyViews must be > 0');
if (w.cacheHitRatio < 0 || w.cacheHitRatio > 1) throw new Error('cacheHitRatio must be in [0,1]');
if (w.avgBytesPerView <= 0) throw new Error('avgBytesPerView must be > 0');
const egressGb = (w.monthlyViews * w.avgBytesPerView) / 1_000_000_000;
const egressUsd = egressGb * w.cdnEgressUsdPerGb;
const edgeUsd = (w.monthlyViews / 1_000_000) * w.edgeRequestUsdPerMillion;
const originRenders = w.monthlyViews * (1 - w.cacheHitRatio);
const originUsd = (originRenders / 1_000_000) * w.originRenderUsdPerMillion;
const transformsUsd = ((w.monthlyViews / 1_000_000) * w.imageTransformsPerMillionViews / 1000) * w.imageTransformUsdPerThousand;
const totalMonthlyUsd = egressUsd + edgeUsd + originUsd + transformsUsd + w.monthlyCiBuildUsd + w.observabilityUsd;
return { totalMonthlyUsd, cpmUsd: totalMonthlyUsd / (w.monthlyViews / 1_000_000), egressUsd, originRenders };
}
const result = costPerMillionViews({
monthlyViews: 100_000_000,
avgBytesPerView: 2_000_000,
cacheHitRatio: 0.995,
cdnEgressUsdPerGb: 0.03,
edgeRequestUsdPerMillion: 0.5,
originRenderUsdPerMillion: 15,
imageTransformUsdPerThousand: 0.5,
imageTransformsPerMillionViews: 5_000,
monthlyCiBuildUsd: 250,
observabilityUsd: 500,
});
console.log(result);With those illustrative inputs, egress is roughly 200 TB per month and often dominates the bill. At $0.03 per GB, egress alone is about $6,000 per month, or $60 per million views. Framework compute only becomes the headline when cache hit ratio falls or image transforms are uncapped. This is why static site generator benchmark results must be paired with CDN and image-pipeline economics.
Core Web Vitals: JavaScript Budgets Decide INP
All four stacks can produce excellent Core Web Vitals. The risk profile differs. Hugo and Eleventy generally ship minimal client JavaScript. Astro ships only island code. Next.js can be fast but carries React hydration and routing costs unless the page is carefully composed.
| Stack | Typical client JS risk | Main CWV concern |
|---|---|---|
| Hugo | Very low | Large unoptimized images |
| Eleventy | Low to moderate | Plugin and analytics creep |
| Astro | Low to moderate | Too many hydrated islands |
| Next.js | Moderate to high | Hydration, middleware, dynamic rendering |
Optimize the LCP image with dimensions, fetchpriority, and AVIF/WebP. Defer non-critical scripts, self-host fonts, and audit third-party tags quarterly. For conversion rate optimization, test with real-user monitoring, not only lab Lighthouse runs.
Edge Caching and Invalidation: The Real Enterprise Architecture Test
At 100M views, cache key design is architecture. Avoid Vary: Cookie on anonymous landing pages; it fragments the cache and destroys hit ratio. Put personalization behind client-side fetches or explicit edge variants. Purge by surrogate key or tag. Use stale-while-revalidate so a cache miss does not become a thundering herd. Rate-limit CMS webhooks and serialize revalidation to prevent invalidation storms.
For Next.js, verify the route is statically rendered or ISR-backed. For Astro, Hugo, and Eleventy, verify that HTML is not accidentally served with no-store. For every stack, test from multiple geographies and inspect cache status headers at the edge, not just at the origin.
Production Checklist for 100M-View Landing Pages
- Measure P50, P75, and P95 TTFB by cache status and region.
- Target 99%+ HTML edge hit ratio for anonymous pages; 99.5%+ for aggressive scale.
- Set
s-maxageandstale-while-revalidateon HTML; fingerprint and immutably cache assets. - Purge by tag or surrogate key, never wildcard purge the whole site during a campaign.
- Benchmark builds with real CMS data, not placeholder Markdown.
- Keep client JavaScript and third-party tags on a budget.
- Compress and resize hero images at the image CDN; cap transform variants.
- Load test at 6x average RPS and include cache-cold cases.
- Model cost per million with egress, transforms, ISR writes, CI, and observability.
- Add RUM and synthetic checks for LCP, INP, CLS, and cache hit ratio.
Decision Matrix: Choose the Constraint You Want to Own
Choose Next.js when the landing page is part of a React product surface, needs incremental static regeneration, or requires app-like personalization and A/B infrastructure. Choose Astro when content velocity, static HTML, and selective interactivity are the priorities. Choose Hugo when build speed and minimal operations dominate a large content corpus. Choose Eleventy when you want JavaScript data pipelines with a static, framework-light output.
All four can serve 100M views. The differentiator is not the generator logo. It is cache discipline, image economics, invalidation design, and JavaScript restraint.
Sources & Technical References
- Next.js caching documentation: https://nextjs.org/docs/app/building-your-application/caching
- Next.js
revalidatePathreference: https://nextjs.org/docs/app/api-reference/functions/revalidatePath - Astro Islands: https://docs.astro.build/en/concepts/islands/
- Astro content collections: https://docs.astro.build/en/guides/content-collections/
- Hugo documentation: https://gohugo.io/documentation/
- Eleventy documentation: https://www.11ty.dev/docs/
- MDN
Cache-Control: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control - RFC 9111, HTTP Caching: https://www.rfc-editor.org/rfc/rfc9111.html
- web.dev TTFB: https://web.dev/articles/ttfb
- web.dev Core Web Vitals: https://web.dev/articles/vitals
- Cloudflare cache documentation: https://developers.cloudflare.com/cache/
- Vercel pricing: https://vercel.com/pricing
- Netlify pricing: https://www.netlify.com/pricing/
- AWS CloudFront pricing: https://aws.amazon.com/cloudfront/pricing/
- HTTP Archive Web Almanac, Performance: https://almanac.httparchive.org/en/2024/performance
Discussion (0)
Continue Reading
More production-grade engineering deep dives and architectural guides

On-Device LLM Inference at 50M Mobile Sessions: llama.cpp vs MLX vs MediaPipe vs ExecuTorch
A production-grade benchmark and architecture guide for running 2B-class LLMs on phones at 50M sessions: prefill latency, thermal-throttled tokens/sec, RSS, and true cost per million tokens versus cloud. Learn when llama.cpp, MLX Swift, MediaPipe, or ExecuTorch wins—and where cloud fallback is still mandatory.

Go Microservices at 1M RPS: go-zero vs Kratos vs go-micro vs go-kit on p99 Latency, Memory, and True Cost per Billion Requests
Most Go framework benchmarks measure a closed loop against an idle server, then publish a number that collapses under real traffic. Here is an open-loop, HdrHistogram-based comparison of go-zero, Kratos, go-micro, and go-kit at 1M requests per second — including the kernel tuning, the memory math, and the engineering amortization that decides which framework actually costs less to own.

Service Mesh at 200k RPS: Istio Sidecar vs Ambient vs Cilium eBPF vs Linkerd — p99 Tail Latency, CPU per Million Requests, and True Cost per Billion
A reproducible 200k RPS benchmark and cost model comparing Istio sidecar, Istio ambient, Cilium eBPF, and Linkerd on p99 tail latency, vCPU-seconds per million requests, and dollars per billion. Includes the PromQL, load-gen manifests, and a Go TCO calculator you can run against your own fleet.