Introduction
Inertia Static brings SSG/ISR to Laravel + Inertia.js without depending on a specific provider. Your pages are rendered at the origin, cached at the edge of any CDN, revalidated by time or on demand, and navigated as a normal Inertia SPA.
It is based on the rendering model of Next.js: the same SSG and ISR ideas, with the same names (revalidate, tags, on-demand revalidation, preview mode), applied to Laravel routes and served by your CDN. See Compared to Next.js for the mapping.
SSG and ISR in short
SSG (Static Site Generation). A page is rendered once and the same HTML is served to every visitor, from a cache, without running your controller again. It is fast and cheap, and it survives traffic spikes, because the origin is not involved.
ISR (Incremental Static Regeneration). SSG that updates itself, page by page, without a new deploy:
- By time. After
revalidateseconds the cached page is stale. The next visitor still gets it instantly, and the page is rendered again in the background (stale-while-revalidate). The following visitors get the new version. - On demand. When your data changes, purge the pages that use it (by tag, URL or prefix), for example when a post is saved. The next visit renders a fresh page.
- Pages that do not exist yet are rendered on their first visit and cached from then on.
| Request | CDN | What happens |
|---|---|---|
1st visit to /blog/hello | MISS | Laravel renders the page and the CDN caches it |
| Next visits | HIT | Served from the CDN, Laravel is not called |
After revalidate seconds | STALE | The cached page is served at once and rendered again in the background |
| Post saved | Tag post:1 is purged; the next visit renders the page again |
Here the cache is your CDN (Cloudflare, Fastly, CloudFront, Varnish, nginx…), and Laravel stays in charge of everything dynamic: forms, sign in, and user data loaded in islands.
It is one Composer package, fydellys/inertia-static:
| Part | What |
|---|---|
| Laravel (PHP) | Route modes, session stripping, cache headers, ETag/304, page JSON endpoint, CDN drivers, purge queue, Artisan commands |
Client (JavaScript, shipped in vendor/fydellys/inertia-static/js) | Plugin for Inertia's HTTP client (Vue and React, Inertia 2 and 3) and a Vite plugin. No npm package to install |
Render modes
| Mode | Behaviour |
|---|---|
dynamic | Default. Plain Inertia, never cached. |
isr | Rendered at the origin, cached by the CDN, revalidated by time (revalidate) and/or on demand (tags, URLs, prefixes). |
static | Same as isr, for pages known in advance: a parameterized static route must list its pages, so inertia-static:warm can pre-render all of them on deploy. |
Why not just set Cache-Control?
Because caching Inertia pages at the edge has sharp edges, and the package handles each of them:
- Private data in a shared cache. Sessions,
authshared props, flash messages and cookies end up in cached HTML. The package removes the session pipeline, whitelists shared props, dropsSet-Cookieand detects access to private data while rendering (Security). - One URL, two representations. Inertia answers HTML and JSON on the same URL (varying on
X-Inertia). Most CDNs ignoreVary, so a visitor can get JSON instead of HTML, which is inertia#1923. The page JSON gets its own URL:/__inertia/{assetVersion}/{path}.json(How it works). - Degraded HTML. When SSR is down, Inertia falls back to a client-side shell. Caching it hurts SEO for hours, so it is never cached.
- Purging is provider-specific. One API for every CDN driver, purges sent from a queue, and a tag registry for CDNs without tag purges (Revalidation).
Requirements
| Version | |
|---|---|
| PHP | 8.3+ |
| Laravel | 12.x, 13.x |
| inertiajs/inertia-laravel | ^2.0.16 or ^3.0 |
| @inertiajs/vue3 or @inertiajs/react | ^2.3 or ^3.0 (Vue 3.5+, React 18+) |
| Inertia SSR | optional (compatible, not required) |