Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save shakyShane/0eff20b872aa1a31ee2cb77058f896ac to your computer and use it in GitHub Desktop.

Select an option

Save shakyShane/0eff20b872aa1a31ee2cb77058f896ac to your computer and use it in GitHub Desktop.

Homepage as static output vs self‑hosted Next (why the “not trivial in prod” warning aged well)

TL;DR: We already said self‑hosting Next for real traffic is not plug‑and‑play—especially if you want defense‑in‑depth at the edge (nginx allow‑lists, document caching, hiding Node). Recent work on this stack bore that out: caching alone spans nginx, HTTP semantics, Next’s output model, and (if you enable them) Cache Components. For a homepage that is not dynamic—many pre‑renderable variants, no request‑time personalization—the right tool is usually static build output + a CDN (or static bucket). You avoid runtime Node, Express, RSC flight plumbing, and the whole class of cache‑correctness debates that only exist when HTML is generated on demand.

This note is for folks who were on the fence when we chose static output for the homepage and heard pushback that “it’s all easy if we just use Next.”


What this repo already is (baseline complexity)

The nextdocker-style setup is intentional engineering, not a default next start:

  • nginx first: deny‑by‑default routing; only explicit paths reach Node; static assets short‑circuited at the edge.
  • Express + custom Next handler: not Vercel’s hosted integration path; you own HTTP edge cases, timeouts, payload sizes, observability split (nginx logs vs app logs).
  • Edge proxy_cache (where enabled): a second cache layer with its own TTL rules, keys, and interaction with upstream Cache-Control and Vary.
  • Operational rules: new routes and Next internals must be wired into nginx or they 404 at the edge—there is no “Next discovered it, therefore the internet can reach it.”

That is appropriate for a stack where you want Node off the raw internet and policy at the edge. It is heavy for a use case that doesn’t need any of it.


What we surfaced once “core Next” behavior met that stack

Even before optional features, App Router + RSC in production adds non‑obvious HTTP behavior:

  • GET /?_rsc=… flight traffic is normal; it hits the same path classes as documents and participates in edge caching / variants in ways that don’t match a mental model of “one URL = one cache entry.”
  • Partial Prerender / shell responses can advertise long document s-maxage while inner caching semantics differ—so “the page should have refreshed” can be true at the framework layer and false at the edge unless you design nginx and Next headers together.
  • Cache Components (cacheComponents, 'use cache') were deferred while we stay on Express + custom handler: not because the feature is “fake,” but because it adds another cache story on top of nginx + document caching—low payoff during this migration phase.

None of this is insurmountable. It is the kind of work you sign up for when you treat Next as a self‑hosted origin behind a strict reverse proxy.


The homepage is a different job

For the homepage specifically, the product reality we agreed on is closer to:

  • Lots of variants (locales, experiments, campaigns, etc.), but
  • Content is known at build time (or can be generated into finite static artifacts), and
  • No requirement for per‑request server rendering, auth‑gated HTML, or dynamic data on the critical path.

In that world:

  • Static output (HTML/CSS/JS + immutable hashed assets) maps cleanly to CDN caching, WAF/rate limits at the edge, and predictable invalidation (new deploy = new files).
  • You do not need an always‑on Node pool to assemble HTML for every visitor.
  • You do not need to reason about RSC, flight requests, or dual caches to ship a fast homepage.

That isn’t a knock on Next as a framework—it’s matching operational weight to requirements.


Connecting back to the earlier conversation

In prior discussions, the pushback sounded like: “Next makes this trivial; self‑hosting is a solved problem.”

What we’ve actually seen while trying to support Next seriously behind nginx + Express:

  • Prod is not next dev.
  • RSC is not “just JSON.” It’s HTTP, caching, routing, and upgrade sensitivity at the edge.
  • “We’ll add caching later” is not one switch—it’s policy, keys, Vary, TTL, and who invalidates what.

That validates the stance that homepage = static was not conservative for its own sake—it was avoiding a class of problems we don’t need to pay for.

The full nextdocker pattern remains valuable where we need a dynamic app behind a strict edge. For a mostly static surface, static build output remains the right tool.


One paragraph you can forward

We said self‑hosting Next with a strict nginx front isn’t “drop in and forget,” and our own integration work reinforced that: routing allow‑lists, edge caching, RSC traffic, and document vs segment cache semantics all have to be designed together. For the homepage—many variants but no dynamic HTML requirement—static pre‑rendered output avoids runtime Node, Express, and the entire RSC/cache interaction surface. That doesn’t invalidate Next for dynamic product surfaces; it’s why we split the problem: static where we can, full stack only where we must.

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