Ultra-fast cache-first reverse proxy for dynamic SSR workloads.
wait0 serves cached HTML instantly and revalidates in the background.
It is designed for Next.js/Nuxt.js and other dynamic origins where latency and origin offload matter.
- GitHub: https://github.com/devforth/wait0
- Docker Hub: https://hub.docker.com/r/devforth/wait0
- Instant cached responses with background revalidation (SWR-like flow)
- Built for dynamic pages, not static asset caching
- RAM + LevelDB cache tiers for low latency and persistence
- Async invalidation API with bearer auth and tag support
- Built-in Basic-Auth dashboard for stats, charts, and invalidation
- Warmup and sitemap discovery to reduce cold-start misses
- Explicit response marker
X-Wait0(hit,miss,bypass, etc.)
- Create
wait0.yaml:
storage:
ram:
max: '100m'
disk:
max: '1g'
server:
port: 8082
origin: 'http://localhost:8080'
rules:
- match: PathPrefix(/)
priority: 1
expiration: '1m'- Run
wait0via Docker:
docker run --rm -p 8082:8082 \
-v "$(pwd)/wait0.yaml:/wait0.yaml:ro" \
devforth/wait0:latest- Alternative: build a tiny wrapper image with your config baked in.
Dockerfile:
FROM devforth/wait0:latest
COPY wait0.yaml /wait0.yaml
EXPOSE 8082Build and run:
docker build -t my-wait0:latest .
docker run --rm -p 8082:8082 my-wait0:latest- Send requests through wait0:
curl -i http://localhost:8082/First request is usually X-Wait0: miss, subsequent requests are usually X-Wait0: hit.
curl -i \
-X POST "http://localhost:8082/wait0/invalidate" \
-H "Authorization: Bearer ${WAIT0_INVALIDATION_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"paths":["/products/123"],"tags":["product:123"]}'Returns 202 Accepted and processes invalidation asynchronously.
Authorization is scope-based (invalidation:write) to support least-privilege tokens and future API growth without changing token model.
curl -i \
"http://localhost:8082/wait0" \
-H "Authorization: Bearer ${WAIT0_STATS_TOKEN}"Returns 200 OK with cache/memory/refresh/sitemap metrics snapshot.
Authorization is scope-based (stats:read).
Set dashboard credentials in environment:
export WAIT0_DASHBOARD_USERNAME='ops'
export WAIT0_DASHBOARD_PASSWORD='change-me'Open:
http://localhost:8082/wait0/dashboard
Notes:
- The dashboard is Basic-Auth protected and disabled when either env variable is missing.
- The dashboard reads stats and triggers invalidation through server-side bridge handlers.
- Bearer tokens are not exposed to browser JavaScript.
- Dashboard invalidation is CSRF-protected (same-origin + token check) and dashboard routes are rate-limited per IP.
| Guide | Description |
|---|---|
| For Developers | Build/test commands, config reference, runtime options |
| API Endpoints | Proxy behavior, invalidation API, schemas, status codes |
SSR frameworks like Next.js/Nuxt usually output versioned static asset names (for example, app.abc123.js).
After a redeploy, HTML references switch to new filenames (for example, app.def456.js), and old files are commonly removed.
If stale HTML is still cached, clients can receive pages that point to missing assets. Typical symptoms are broken UI, hydration failures, and partial renders.
To reduce this risk, wait0 clears disk cache on startup by default (WAIT0_INVALIDATE_DISK_CACHE_ON_START=true).
That behavior makes rollout safer because old HTML is not reused across deploy generations.
If you do not restart between deploys, proactively refresh cache using invalidation and warmup for critical routes.
- Request pipeline: RAM cache -> disk cache -> origin.
- Cache key is path-only (query and fragment are ignored for cache identity).
- Only
GETrequests are cache candidates. - Only origin
2xxresponses are stored. - For stale entries (rule
expiration), wait0 serves cached response immediately and revalidates asynchronously. - For origin non-
2xx, wait0 skips caching and evicts any existing key for that path. - Invalidation is asynchronous: accept request -> resolve keys by
pathsandtags-> delete keys -> recrawl in background.