Static files
Static files in the Deploy guide.
static takes a StaticOptions. Paths are decoded once, checked for dot segments, NUL bytes and backslashes, and
resolved with realpath below the real directory, so neither ..%2f nor a symlink leads out. Only GET and HEAD are
answered; a path that is not a file falls through to the handler. When security.allowedHosts is set, the Host is checked
before any file is read: a refused Host gets the handler's 400 for every path (see Platform host lists).
StaticOptions |
Default | Meaning |
|---|---|---|
dir |
required | directory to serve |
prefix |
"/" |
URL prefix it is mounted at (/app for a build with base: "/app/") |
immutable |
a file name with a hash segment of 8 or more characters (and a digit, unless it sits under assetsDir), or a font under fonts/google/ (named by its content hash) |
Cache-Control: public, max-age=31536000, immutable |
assetsDir |
"assets" |
the build's assets folder (Vite's build.assetsDir); the default immutable rule counts a hashed file under it |
maxAgeSeconds |
0 |
max-age of every other file, which always revalidates (must-revalidate) with its ETag |
pages |
none | the prerendered pages and how each is answered: static (the file, with the app's headers) or handler (rendered per request, the file is never read). The build fills it; see the Vercel section |
index |
none | file served for a directory ("index.html" for a static site) |
extensions |
none | extensions tried for a path without one ([".html"] serves /about from about.html) |
dotfiles |
"deny" |
"allow" serves paths with a segment that starts with a dot |
wellKnown |
security.txt, assetlinks.json, apple-app-site-association, change-password, openid-configuration |
names served from public/.well-known/<name>; no subpaths. Other dotfiles, .env* included, stay denied |
precompressed |
true |
use file.br, file.zst or file.gz next to the original when the client accepts it |
Every response has a weak ETag, Last-Modified and X-Content-Type-Options: nosniff; If-None-Match and
If-Modified-Since give 304; a single Range gives 206 (not on compressed variants); Vary: Accept-Encoding is set on
compressible types, whether or not the server compresses.
Precedence. public/ is served before the handler, so public/robots.txt answers /robots.txt and a route at the
same path is never reached. When a route and a public file must answer different things, give them different paths. To run
the global middleware for these files (an auth gate on public/ or on the build's assets), set
runMiddlewareForStaticFiles: true; see Files and the global middleware.
Dot paths. Any path with a dot segment is refused, except /.well-known/<name> for a name in wellKnown, which
is served as a plain file (apple-app-site-association and openid-configuration as JSON). A traversal spelled with
encoded dots or slashes (%2e%2e, ..%2f, %252e%252e) is refused, or left to the handler; it never reads a file
outside the folder.
Prerendered pages. The generated Node server passes index: "index.html" and extensions: [".html"]: /about
answers about/index.html (or about.html) and / answers index.html, with the static cache headers above. The
generated Bun and Deno servers answer the same files. Their static handling is narrower than Node's: no Range, no
If-Modified-Since, no precompressed siblings (deferred, see Other runtimes).