Skip to content

Instantly share code, notes, and snippets.

View shakyShane's full-sized avatar

Shane Osbourne shakyShane

View GitHub Profile

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)

Deferring Next.js Cache Components (Express + custom handler phase)

One-liner: Cache Components are deferred until we’re off Express + custom Next; nginx + our existing Next usage are enough for now.


Decision

We are not adopting Next.js Cache Components (cacheComponents in next.config, 'use cache', cacheLife, etc.) while the application runs Express with the custom Next request handler.

Next.js behind closed-by-default nginx — notes for SRE / backend

What you’re optimizing for

Traffic hits nginx first. Node/Express is not on the raw internet path in the sense that:

  • Unknown URLs stop at nginx (public/ file or 404); they never reach Express.
  • Only explicit locations forward to the app (documents, /_next/image, health/debug, etc.).
  • Heavy caching (especially /_next/static/ from disk, document proxy_cache for allow‑listed HTML/RSC paths) reduces origin load and absorbs a lot of read traffic before it touches Node.
@shakyShane
shakyShane / duckai-perf-3g-6x.md
Created March 24, 2026 07:56
Duck.ai Performance Comparison — 3G + 6x CPU — 2026-03-24

Duck.ai Performance Comparison — 3G + 6x CPU

Date: 2026-03-24
Tool: Playwright (Chromium, headless)

Production (current) Candidate
URL 030c2a27bdaf.ngrok.app 5cc2bb9fdebc.ngrok.app

@shakyShane
shakyShane / duckai-perf-baseline.md
Last active March 24, 2026 07:50
Duck.ai Performance Baseline — 2026-03-24

Duck.ai Performance Baseline

Date: 2026-03-24
Tool: Playwright (Chromium, headless)

Candidate Production (current)
URL 2e22761cd923.ngrok.app 2a1e017fcdab.ngrok.app

config-gen

Generate RequireJS Optimizer configuration for Magento 2 website based on real-world usage.

Step 1 - Download the binary

config-gen is packaged as a single binary (currently only osx) to enable easy usage, just check the releases page and download the latest.

config-gen

Generate RequireJS Optimizer configuration for Magento 2 website.

Step 1 - Download the binary

config-gen is packaged as a single binary (currently only osx) to enable easy usage, just check the releases page and download the latest.

struct AppState {
config: Arc<Mutext<Vec<String>>>
}
///
/// these handlers can be running on any number of threads
///
fn handler(req: &HttpRequest<AppState>) -> Box<Future<Item = HttpResponse, Error = ()>> {
// clone the Arc here so it can be safely moved into the closure
impl M2PresetOptions {
pub fn get_opts(prog_config: ProgramConfig) -> Option<M2PresetOptions> {
serde_yaml::from_value(prog_config.get_opts("m2")?).ok()?
}
}
///
/// A simple way to apply both a predicate
/// and default value when dealing with an option.
///
fn is_legal(age: Option<usize>) -> bool {
age.map(|x| x >= 18).unwrap_or(false)
}