Steven's Knowledge

Best Practices

Production static site hosting - build optimization, performance, observability, cost, security

Best Practices

The platform does a lot for you, but real production still needs discipline. These habits keep your site fast, cheap, and secure.

Build Times Matter

Slow builds slow PRs. Watch:

PracticeEffect
Lock files committedReproducible installs; cache works
npm ci not npm install in CIFaster, deterministic
Build cache enabledSubsequent builds skip unchanged dependency installs
Code splittingSmaller chunks; parallel build
Turborepo / Nx for monoreposOnly build what changed
Pre-built native modulesAvoid compilation during build

For monorepos: per-app deploys, not "rebuild everything when any line changes anywhere."

Performance

Modern static hosting + frameworks make a fast site trivially achievable. Don't undo it:

HabitWhy
Use the framework's <Image> componentAuto-optimization
Lazy-load below-the-fold imagesSmaller initial payload
priority flag on hero / LCP imageDon't defer the critical render
Set explicit width and heightAvoid layout shifts
Preload critical fonts; use font-display: swapFaster perceived render
Code-split routesDon't ship admin code to logged-out users
Static export where possibleEdge CDN, no function invocation
Limit third-party scriptsEach one is a perf and privacy cost
Avoid client-side data fetching on every pageSSR or static where possible

Measure with Lighthouse, PageSpeed Insights, and Real User Monitoring (Vercel Speed Insights, Cloudflare Web Analytics, your own RUM via Tracing).

Core Web Vitals: LCP < 2.5s, CLS < 0.1, INP < 200ms. Modern frameworks help; don't sabotage.

Bundle Size Discipline

JavaScript is the heaviest part of most modern sites:

  • @next/bundle-analyzer or platform equivalent — visualize what's in your bundle.
  • Big libraries are big wins to remove: moment.js → date-fns; lodash → small utilities; entire UI kit when you use 3 components → import per-component.
  • Dynamic imports for heavy components (charts, maps, editors).
  • treeshake: true is the default; verify with the analyzer.

The cheapest win is removing, not optimizing.

Observability

What to monitor:

SignalWhy
Build success rateFailed builds = blocked deploys
Build timeTrending up = inefficiency creep
Function invocations / costCatch runaway SSR loops
Function error rateEdge / serverless errors
BandwidthEspecially on bandwidth-billed platforms
Real User Monitoring (Web Vitals)What real users experience
Page-load JS sizeTrending up = code bloat

Each platform ships analytics. For deeper analysis, integrate Sentry for errors, Cloudflare Web Analytics or Plausible for privacy-respecting metrics, Vercel Speed Insights if on Vercel.

Caching: Headers and CDN

Static assets should be cacheable forever (with hash in filename):

/_next/static/abc123.js → Cache-Control: public, max-age=31536000, immutable
/_next/static/def456.css → Cache-Control: public, max-age=31536000, immutable

Frameworks do this automatically. Don't override unless you have a reason.

Page HTML (SSR / ISR):

Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=86400
  • 0 in browser (always check).
  • 300s shared CDN cache.
  • Serve stale for 24h while revalidating in background.

See CDN for the deeper treatment.

Security Headers

A baseline _headers file (Netlify / Cloudflare Pages):

/*
  Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()
  X-Frame-Options: SAMEORIGIN

/admin/*
  Cache-Control: no-store, no-cache

For full Content-Security-Policy, use securityheaders.com to verify yours. CSP is finicky but worth it for production.

Cost Control

Watch:

Cost axisMitigation
BandwidthCloudflare Pages (free); cache aggressively; image optimization
Function invocationsStatic where possible; cache responses
Build minutesFaster builds; fewer triggered builds
Image transformationsCache; limit infinite-variants
Team membersPro tiers often per-seat; review usage

For ~99% of small-to-mid teams: Cloudflare Pages free tier covers it. Vercel free tier covers many small projects too. You start paying when you hit Pro features (team, custom domains beyond N, advanced analytics).

Vercel Hobby tier prohibits commercial use — small companies on Hobby is a TOS violation. Use Pro or move to Cloudflare Pages.

Backups and Disaster Recovery

The platforms run your CDN; you don't back up the static assets (they rebuild from your repo). But:

  • Your domain DNS is critical — see DNS.
  • Account access — multi-admin, MFA, recovery email.
  • Repo as source of truth — if the platform disappears, your code's in git.
  • Database / object storage backups — separate concern; see relevant pages.

For mission-critical, practice deploying to a second platform (e.g., have a Netlify config ready alongside Vercel). Switching DNS takes a few minutes if you've practiced.

Common Pitfalls

PitfallSymptomFix
Secrets in NEXT_PUBLIC_*Visible in browser bundleServer-only env vars
dynamic = 'force-dynamic' on every pageEvery request hits a functionAudit; static where possible
Ginormous JS bundleSlow page loadBundle analyzer; split / remove
await fetch() in getServerSideProps for static-able dataPer-request when ISR would doUse generateStaticParams or revalidate
Image without <Image> componentUnoptimized, no resizeUse the framework's Image
Preview deploys leak unfinished features publiclyDiscovered by usersLock previews behind auth
Forgot to set production env varsBuild fails or runs with placeholdersVerify before promoting
Building from a non-default branchDeploys old codeSet production branch correctly
Slow build because no cacheDevs frustratedVerify build cache is enabled
Missing security headersLower security score_headers file with baseline

When to Outgrow

You've outgrown a static host when:

  • Serverless function costs dominate your bill — move to containers (Render, Fly, Railway, K8s).
  • You need full Linux containers (heavy native deps, custom runtime).
  • You're orchestrating backend services beyond simple API endpoints.
  • Strict data residency they don't support.
  • You want to own the infrastructure end-to-end.

For most teams: you'll stay on a static host for a long time. Vercel and Netlify customers reach IPO scale on these platforms.

Migration Strategies

Between platforms (e.g., Vercel → Cloudflare Pages):

  1. Set up the new platform alongside the existing one.
  2. Deploy to a preview URL on the new platform.
  3. Test thoroughly — adapter differences can surface bugs.
  4. Add new platform's domain as additional, then primary.
  5. Cut over DNS (low TTL beforehand).
  6. Keep old running for a week as a fallback.
  7. Decommission.

The biggest gotcha: framework-specific features that work differently per platform. Image optimization, ISR, middleware — verify each before cutting over.

Checklist

Production static site hosting checklist

  • Production branch protected; deploys only from main
  • Preview deploys behind auth if site contains anything sensitive
  • Custom domain with HTTPS; HSTS preload submitted for prod
  • Per-environment env vars; secrets encrypted; never in NEXT_PUBLIC_*
  • Framework Image component for all <img>-like uses
  • Security headers baseline (_headers or vercel.json)
  • Real User Monitoring enabled
  • Build cache enabled; build times measured
  • Bundle size analyzed; major bloat removed
  • Render modes intentional: static > ISR > SSR
  • Rollback path tested
  • Monitoring on function invocations + errors
  • Cost dashboard reviewed monthly
  • DNS TTL prepared for emergency cutover
  • Account: MFA enforced; 2+ admins
  • Documented "what to do if the platform is down" runbook

On this page