Configure
Declare the assets directory with theassets key in the wrangler.json file:
wrangler.json
assets accepts:
Deploy refuses any other key. Every Judt App has a
main entry module, so every App has a Worker beside its assets.
All files in the assets directory are public. Don’t put Worker source or credentials there. Asset files can be of any type, and the App’s source limits apply to them: 1 MiB for each file, and 32 MiB and 2,048 files and directories in total. For more information, see The App contract.
How celld routes a request
celld decides whether an asset or the Worker answers each request. It applies the following rules in order, and the first rule that matches decides:- A request with a method other than
GETorHEADgoes to the Worker. - A request whose path matches
run_worker_firstgoes to the Worker. - The rules in the
_redirectsfile apply in file order. The first rule that matches wins. html_handlingfinds the file. For example,/aboutserves/about.html, and/about/serves/about/index.html. A request for a different spelling of the path gets a307redirect.- If no file matches and the request is a browser navigation, then
not_found_handlingapplies. A browser navigation has theSec-Fetch-Mode: navigateheader, and the App must have acompatibility_dateof2025-04-01or later.curldoesn’t send this header, so its request goes to the Worker.single-page-applicationreturns/index.htmlwith status200.404-pagereturns the nearest404.htmlfile with status404. - All other requests go to the Worker.
Serve a single-page app with an API
To serve a single-page app beside an API, do the following:- Set
"run_worker_first": ["/api/*"], so that API requests never get an asset or the single-page app’s HTML. - Return a JSON
404response from the Worker for unknown API routes. - Don’t write Worker routes for files that exist. The Worker doesn’t see requests for them.
Serve assets from the Worker
env.ASSETS.fetch() takes a request or a URL and returns the asset. It accepts only GET and HEAD requests, and it applies not_found_handling. Use it to put a check in front of a set of files:
index.js
admin.html, and ask for it without the extension, such as /admin. html_handling also applies to env.ASSETS.fetch(), so /admin.html gets a 307 redirect to /admin.
In celld 0.6.3,
env.ASSETS.fetch() doesn’t find a nested index.html file. /admin/ gives a 404 response, and /admin and /admin/index.html give a 307 redirect.HTMLRewriter. If you put JSON into a <script> element, escape each < character in the JSON.
Set headers and redirects
To set response headers or redirects for assets, add a_headers or _redirects file at the top of the assets directory. Each file has a limit of 100 KiB. celld reads these files but doesn’t serve them.
public/_headers
public/_redirects
- A redirect status is
301,302,303,307, or308. Status200is an internal rewrite. - The
_headersfile can’t setconnection,content-length, ortransfer-encoding.
Caching
celld handles caching for assets as follows:- The
ETagheader is the SHA-256 hash of the body, and a request with a matchingIf-None-Matchheader gets a304response. There’s noLast-Modifiedheader. - The default
Cache-Controlheader ispublic, max-age=0, must-revalidate, so browsers check for a change on each request. - Range requests work and return
206. - There’s no compression and no CDN, so keep files small.
immutable cache in the _headers file. Keep HTML on the default, so that visitors see a deploy at once.
Unsupported files
Deploy refuses an assets directory that has any of the following:- An
.assetsignorefile at the top of the directory - A
_worker.jsfile or directory at the top of the directory - Symbolic links
- File names that aren’t UTF-8
Build a frontend without a build step
We recommend code that browsers run directly. It needs no build, and the dev server serves each change at once. The following techniques work without a build:- HTML and modern CSS, such as custom properties, grid, nesting, and
:has(). - Browser ES modules, such as
<script type="module" src="/app.js">, with relative imports. - Web components (
customElements.define) or a small template function for views. - A library from a CDN, loaded with an import map. Use exact versions. The App then depends on the CDN.
build script that writes the assets and the Worker’s entry module to dist/. Then point assets.directory and main into dist/. For more information, see npm packages and builds.