Cloudflare Workers now supports serving static assets (as part of the effort to unify Cloudflare Workers and Pages).
Note
Both Cloudflare Workers and Pages can now host full-stack and frontend applications. While Cloudflare Workers provides a better integration with the rest of the Cloudflare developer platform, Cloudflare Pages does have some convenient features such as deploy hooks and branch deploy controls. Review the compatibility matrix to decide which to use.
Note
Cloudflare Workers provides batteries-included guides for full-stack frameworks (with server-side rendering and other hybrid rendering modes). Check out the docs for hosting frameworks on Cloudflare Workers with static assets if that's what you're looking for. This guide focuses on hosting single-page applications.
-
Run
npm create cloudflare@latest
to create a Cloudflare Workers project locally with . Accept all the basic options (Hello World example
,Hello World Worker
, etc.) -
Run `cd <PROJECT_NAME> to step into your Workers project directory.
-
Create your single-page application within the Workers project. For instance, run
npm create vite@latest my-spa-app -- --template react
,npm create vite@latest my-spa-app -- --template vue
,ng new <project-name>
. -
If you don't want any API routes or access to server-side functionality with Workers, you can delete the
src/index.ts
file and remove the entrypoint fromwrangler.toml
orwrangler.json
. This will omit Workers from being run and only your single-page application will be served. -
Configure your
wrangler.toml
/wrangler.json
file of your Workers project to add the assets binding. The directory must be thedist
folder of the SPA application. In the example below,spa-app
is the folder containing our single-page application project and the build step of our SPA outputs contents to thedist
folder. Refer to the examples in this repository for more guidance if needed.This step is important to configure the fallback of your single-page application, since your single-page application handles the routing client-side (there is only 1 index.html file and routes are handled with JavaScript in the user's browser). If this is not configured, direct request to
example.com/some-page
will not be properly handled.- If you aren't running Workers code and have deleted
src/index.ts
as specified in step 4, you must specifynot_found_handling
to besingle_page_application
. - If you are running Workers code, you must specify
binding
which we'll use in our Cloudflare Worker.
//if using wrangler.toml assets = { binding = "ASSETS", directory = "./spa-app/dist", not_found_handling = "single-page-application" } //if using wrangler.json "assets": { "binding": "ASSETS", // only required when there is Workers code to configure navigation fallback "directory": "./spa-app/dist", "not_found_handling": "single-page-application" // only required when there is no Workers code }
- If you aren't running Workers code and have deleted
-
If you aren't running Workers code and have deleted
src/index.ts
, no other steps are required. If you are running Workers code, you must handle the fallback behavior in your Worker code directly using the asset binding.export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname.startsWith("/api/")) { // TODO: Add your custom /api/* logic here. return new Response("Ok"); } // Passes the incoming request through to the assets binding. // No asset matched this request, so this will evaluate `not_found_handling` behavior. return env.ASSETS.fetch(request); }, };
-
Change your
dev
,start
anddeploy
scripts in your Workers project'spackage.json
. These should first perform a build of the child single-page application project before running the Workers dev commands. In the below example,spa-app
contains our React/Angular/Vue/SPA app and callsnpm run build
(or otherwise configured) to build the SPA project.{ "name": "spa-on-workers-assets", "scripts": { "deploy": "cd spa-app && npm run build && cd .. && wrangler deploy", "dev": "cd spa-app && npm run build && cd .. && wrangler dev", "start": "cd spa-app && npm run build && cd .. && wrangler dev", ... }, ... }
When developing locally, you may find yourself wanting some level of hot reloading. The above scripts don't include this. Instead, you can open a separate terminal instance, navigate to the single-page application project, and run npx vite build --watch
in order to build your single-page application project on every save. You'll then be able to refresh your Workers application in the browser and it will be serving the new version of your application.
Pro tip: You can use npx wrangler types
to generate types for your Workers binding if your project is configured with TypeScript.