[]previous and next page

Prerendered pages and the app's rules

A prerendered HTML file cannot see the request: not its host, its cookies or its nonce.

For agentsUpdated

A prerendered HTML file cannot see the request: not its host, its cookies or its nonce. So a page is served as a file only when nothing that runs at request time can change its answer. The decision table is in Which prerendered pages the handler answers.

The build applies these rules:

  • a global middleware in the entry makes every prerendered page static (via handler): the handler runs the middleware first, then renders the page, so the answer is the one a dynamic page gets;
  • the entry's security.headers and security.csp are applied to the files of the pages that stay files, the way the handler applies them;
  • an entry value the build cannot read (a call, an import, a computed key) makes every prerendered page static (via handler), because the build cannot know what the value does.

Check the route table printed by dusk build and staticPages in dist/dusk-manifest.json to see which paths require the handler.

The host list also applies to files . The Node static layer (serve({ static }), dusk start, the generated server.mjs) and the Bun and Deno layer check security.allowedHosts before they read a file. A request whose Host the list refuses is not answered from the folder: the handler answers its own 400, for a prerendered page, a file in public/, a hashed asset and a path with no file alike, so a caller cannot tell which files exist. With no host list the file path does no host work at all.

The platform routes follow the same list when the build can read it as a literal: Vercel sends a refused Host to the function before the filesystem, and the Cloudflare Worker runs for every request and answers a refused Host through the handler. The platforms' host expression accepts the same hosts as the handler's rule. Netlify documents no Host condition in its redirect or function declarations, so there the host list is checked by the function for the requests it answers, and a file the CDN answers first is not. The limits are in the deploy guide.

Files and the global middleware#

By default a public/ file, a build asset and a prerendered page's file are answered before the global middleware, so a file costs no code and the middleware never sees it. Set runMiddlewareForStaticFiles: true in the handler options. With it the global middleware runs first for every file the server would send. It can refuse the file (an auth gate), redirect it, or set headers and cookies that go out with it; the framing headers (content-type, content-length, the validators) stay the server's. The Node server (dusk start, serve()), the Bun and Deno servers and the Cloudflare Worker honor it. Vercel, Netlify and the static export answer files before any code runs, so the build warns (it names the option) and the middleware does not run for them. In dusk dev, Vite answers the files, so the middleware does not run for them there either. Route and folder middleware never run for a file: they belong to pages and API routes. Decide for your app whether the files need the gate before you rely on it.