# Page Caching

Pages in Rebask are cached by default in production. A page cache entry can contain either a
Complete HTML representation of the page or a static HTML shell with dynamic holes that are
resumed at runtime.

## Page cache layers

Rebask builds reusable shells for dynamic routes. A shell contains static HTML and postponed state
that tells React how to resume the dynamic parts for a request at runtime. See
[What a resume stores](../advanced.md#what-a-resume-stores) to learn more.

| Entry              | Params baked into the entry                                                                   | When                               | Purpose                                                             |
| ------------------ | --------------------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------- |
| Shared shell       | None                                                                                          | Build time                         | Fallback for any path that matches a dynamic route                  |
| Intermediate shell | Params declared by a layout's [`prerenderParams`](../routing/introduction.md#prerenderparams) | Build time                         | A page fallback for a known parameter combination                   |
| Pathname shell     | All params in the URL pathname                                                                | Build time or background promotion | Resumable output for a path with all route parameters known         |
| Complete HTML      | All params in the URL pathname                                                                | Build time or background promotion | Complete HTML for a cached path that can be served without a resume |

If a shared or intermediate shell contains no dynamic holes, there is nothing to resume. The same
shell serves every matching pathname, so navigating between them only appends the route tree and
does not fetch a new segment.

At build time, Rebask uses the route params known for the render and whether rendering reads
[request I/O](../server-runtime-apis.md). Together, they determine whether it generates a shared
shell, an intermediate shell, a Pathname shell, or Complete HTML:

::page-cache-build-time
::

A Pathname shell and Complete HTML are two representations stored under the same pathname cache key. When rendering completes without accessing request I/O, Rebask stores the result as Complete HTML, which can be served directly. When rendering accesses request I/O, the result is a resumable Pathname shell instead.

::page-cache-runtime
::

### Shared shell

For dynamic pages that allow unknown parameter values, Rebask prerenders the page at build time with no route parameters to produce a shared shell.

::shared-shell
::

This shell can serve any allowed path that matches the page because no parameters are baked into it. Rebask uses it when no more specific page cache entry is available.

The shared shell is keyed by the route file, not by a concrete pathname. The shell is served first, then Rebask resumes the postponed parts of the page for the requested path.

### Intermediate shells

`prerenderParams` on layouts can declare known parameter combinations. Rebask renders an intermediate
shell for each combination and bakes those known parameters into the static HTML:

```bash
app/
  [locale]/
    layout.tsx                 → declares locale values: "en", "es"
    products/[id]/page.tsx     → /:locale/products/:id

Shared shell:       /[locale]/products/[id]
Intermediate shell: /en/products/[id]
Pathname entry:     /en/products/42
```

```tsx
// app/[locale]/layout.tsx — intermediate shells
export function prerenderParams() {
  return { params: [{ locale: "en" }, { locale: "es" }] }
}

// app/[locale]/products/[id]/page.tsx — complete parameter combinations
export function prerenderParams() {
  return {
    params: [
      { locale: "en", id: "1" },
      { locale: "en", id: "2" },
      { locale: "es", id: "1" },
    ],
  }
}
```

For `/en/products/42`, an intermediate shell can contain `locale: "en"` while leaving `42` as a
dynamic hole.

At runtime, when multiple intermediate shells match a request, Rebask selects the most specific one
and resumes any remaining dynamic parameters:

::intermediate-shell
::

The combinations returned by layout [`prerenderParams`](../routing/introduction.md#prerenderparams)
become the available intermediate shell variants. Rebask records them in the build manifest and uses
them to select the most specific shell for each request, while platform adapters use them to
package prerender routes.

> Generating new variants at runtime would make shell keys unpredictable and could lead to an unbounded number of intermediate entries, so unknown combinations use the shared shell instead.

### Pathname shells

Rebask creates a Pathname shell when all route parameters for a page are known and rendering reads request I/O. At build time, this includes pages without dynamic segments and dynamic pages whose page-level `prerenderParams` lists complete parameter combinations.

At runtime, after a less specific shell serves a request, Rebask can render that pathname in the background and store the result under its pathname cache key. When request I/O leaves postponed state, the result is a Pathname shell.

::pathname-shell-promotion
::

Rebask does not promote pages that accessed request I/O to Complete HTML, because doing so could cache data from an individual request (cache poisoning). When a resume is necessary a Pathname shell is created.

Platform adapters can perform this promotion by sending a new upgrade request to Rebask after the shell has resumed. On [Vercel](../production/vercel.md) for example, the adapter sends a request in the background and the platform stores the result in its prerender cache. If an upgrade render accesses request I/O inside a `<Suspense>` boundary, Rebask returns a resumable Pathname shell instead of Complete HTML.

### Complete HTML

This entry is the complete HTML representation for a concrete pathname. It has no postponed state, so Rebask can serve it directly without a resume.

::complete-html
::

A Complete HTML hit is equivalent to serving a static HTML file for that pathname. The complete document has already been generated, so the request only needs to read and return it.

## How a cache hit is chosen

For a normal HTML request, Rebask performs this lookup for a dynamic page:

1. In parallel, look up the pathname entry, such as `ph:<deployment-id>:/products/42`, and the best matching shell entry from the build manifest.
2. If they both missed and the best matching shell was an intermediate shell, look up the shared shell next.
3. If all lookups miss, render the request.

A shell entry is a [PPR hit](../rendering/ppr.md): Rebask serves its HTML and resumes its dynamic parts, or only appends the route tree of the path for a shell that rendered complete. Complete HTML is served directly.

A fresh entry means a cache `HIT`. An entry past its `revalidate` window is `STALE` but still usable so Rebask serves it and schedules a background refresh. An entry past its `expire` window or invalidated by a tag is treated as a `MISS`, so Rebask continues to the next shell or renders the request.

## Page cache keys

| Entry                           | Logical key                                              |
| ------------------------------- | -------------------------------------------------------- |
| Pathname shell or Complete HTML | `ph:<deployment-id>:<pathname>`                          |
| Shared shell HTML               | `ph:<deployment-id>:<route-file>`                        |
| Intermediate shell HTML         | `ph:<deployment-id>:<route-file>:<encoded-shell-params>` |
| Default not-found HTML          | `ph:<deployment-id>:__default_not_found__`               |

Intermediate shells can use encoded params such as `locale=en|category=books`.

Page cache keys are prefixed with a `deploymentId`, generated for each production build. You can learn more about why in [Server Cache Keys](./introduction.md#server-cache-keys).
