What a website serves
File types, addresses, app routing, caching, headers, and what browser storage can be trusted with.
File types
A website can contain these file types. Each file is served with the type its extension names here, never a type guessed from its contents; a deploy with any other extension is refused and names the file.
| Kind | Extensions |
|---|---|
| Pages and data | html, txt, xml, json, webmanifest, pdf |
| Styles and scripts | css, js, mjs, map, wasm |
| Images | svg, png, jpg, jpeg, gif, webp, avif, ico |
| Fonts | woff, woff2, ttf, otf |
| Audio and video | mp4, webm, mp3, wav |
- A path is at most 256 bytes, with no empty,
.or..parts, and no hidden file or folder except a top-level.well-known. - PDFs download rather than open in the browser.
- A site with no
favicon.icogets Benchy’s. Add your own to replace it. - Sizes and file counts are limited per deploy. See Deploy limits.
Addresses and pages
Benchy finds the file for an address in this order:
| Address | Serves |
|---|---|
/ | /index.html |
/about/ | /about/index.html |
/about | The file /about; else a redirect to /about/ when /about/index.html exists; else /about.html |
| Anything else | Your /404.html with status 404, or Benchy’s “Page not found” |
The redirect to /about/ keeps the query string, and it is what makes relative links inside about/index.html work.
App routing
A single-page app that routes in the browser, with addresses such as /products/42, needs every such address to load its index.html. Say so in a benchy.site.json file at the top of the output folder:
{
"fallback": "/index.html"
}- An address with no file and no extension then serves
/index.htmlwith status 200. An address with an extension, such as/app.js, still gets a 404 when the file is missing, so a broken script shows as broken. - Real files and folders always win over the fallback.
benchy.site.jsonis read when you deploy and is not served. It also declares content and forms; see Content and forms.
JavaScript, apps and backends
JavaScript and WebAssembly run in your visitors’ browsers, so games, tools, 3D viewers and other browser apps work. What a website doesn’t have is a server of its own:
- no server functions, API routes or server-side rendering;
- no database, and no visitor sign-in;
- no secrets. Every file is public, so an API key in a published file is a leaked key.
When a site needs a backend, the browser calls one you run elsewhere, such as Convex, Supabase or your own API. Benchy provides two server features itself, both on your site’s own address: editable content and forms. The path /_benchy/ is reserved for them on every site.
Caching
| Files | How long browsers and the CDN keep them |
|---|---|
| HTML pages | Checked on every visit, so a new version shows at once |
Files whose name carries a fingerprint of 8 or more characters, such as app.3f9a1c2b.js | A year. Build tools name files this way, so a change gets a new name |
| Everything else | 5 minutes |
Every file is sent with its fingerprint as its ETag, so unchanged files cost a visitor nothing to check, and audio and video can be streamed and seeked.
Headers
Benchy sets every response header itself. Every page is sent with:
Strict-Transport-Security, so browsers use HTTPS only: two years on benchy.site, one week on a custom domain;X-Content-Type-Options: nosniffandReferrer-Policy: strict-origin-when-cross-origin;Permissions-Policyturning off the camera, microphone and location;frame-ancestors 'self', so only your own site can show your pages in a frame;X-Robots-Tag: noindexwhen the site is kept out of search. See Search engines.
You can’t add or change headers, and _headers or _redirects files from other hosts do nothing. A Content Security Policy in a <meta http-equiv> tag in your HTML still applies.
Cookies and storage
Benchy never sets a cookie on a website, and Benchy’s own sign-in never reaches one. Every site on benchy.site shares the parent domain benchy.site, so a script on one site can set a cookie that every other site receives.
- Don’t treat a cookie,
localStorageorsessionStoragevalue as trustworthy. Use them for things a visitor can harmlessly change, such as a remembered theme or a dismissed notice. - Don’t set a cookie on the parent domain, and don’t rely on reading one set there.
- A custom domain gives the site an origin of its own. Write the site so the advice above holds either way.
When a website is unpublished or suspended, visitors’ browsers are asked to clear what it stored.
What a website can’t do
- Run a service worker. A worker could keep serving a site after it is unpublished, so none are served. Pages still work offline as far as the browser cache allows.
- Take anything but GET and HEAD. Other requests get 405, except form entries sent to
/_benchy/forms/. - Redirect or rewrite on the server, beyond the address rules and the app fallback above. Use an HTML page with a
<meta http-equiv="refresh">tag to send visitors from an old address to a new one. - Use the camera, microphone or location.
Use ← and → to move between pages.