Skip to content

Commit 73e5df6

Browse files
korinnewindsurf-bot[bot]emily-shenGregBrimble
authored
adds new static routing options under run_worker_first (#23008)
* adds new static routing options under run_worker_first * Update src/content/docs/workers/static-assets/index.mdx Co-authored-by: windsurf-bot[bot] <189301087+windsurf-bot[bot]@users.noreply.github.com> * Update src/content/docs/workers/static-assets/binding.mdx Co-authored-by: windsurf-bot[bot] <189301087+windsurf-bot[bot]@users.noreply.github.com> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: windsurf-bot[bot] <189301087+windsurf-bot[bot]@users.noreply.github.com> * Update src/content/docs/workers/static-assets/binding.mdx Co-authored-by: emily-shen <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: emily-shen <[email protected]> * updates main overview page * updates to single-page-application docs * updates configuration docs * updates worker script docs * updates overview page * removes note under vite plugin docs * adds changelog entry for new routing, small updates * Update src/content/docs/workers/static-assets/routing/worker-script.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/worker-script.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/worker-script.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/worker-script.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/binding.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/binding.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/index.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/index.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/index.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/index.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/changelog/workers/2025-06-17-advanced-routing.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/changelog/workers/2025-06-17-advanced-routing.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/changelog/workers/2025-06-17-advanced-routing.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/changelog/workers/2025-06-17-advanced-routing.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/changelog/workers/2025-06-17-advanced-routing.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/changelog/workers/2025-06-17-advanced-routing.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/binding.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/binding.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/worker-script.mdx Co-authored-by: Greg Brimble <[email protected]> * Update diagram to show run_worker_first path matching * Wrap example in TypeScriptExample * Clarify how path patterns must begin * Add examples for static routing * Clarify negative patterns when limited * Clarify support for advanced routing in aside * Update copy for middleware features * Update src/content/changelog/workers/2025-06-17-advanced-routing.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/binding.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/changelog/workers/2025-06-17-advanced-routing.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/index.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/routing/single-page-application.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/wrangler/configuration.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/index.mdx Co-authored-by: Greg Brimble <[email protected]> * Update src/content/docs/workers/static-assets/binding.mdx Co-authored-by: Greg Brimble <[email protected]> --------- Co-authored-by: windsurf-bot[bot] <189301087+windsurf-bot[bot]@users.noreply.github.com> Co-authored-by: emily-shen <[email protected]> Co-authored-by: Greg Brimble <[email protected]>
1 parent f033f14 commit 73e5df6

File tree

9 files changed

+236
-244
lines changed

9 files changed

+236
-244
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: Control which routes invoke your Worker script for Single Page Applications
3+
description: New configuration options for specifying which routes invoke your Worker script.
4+
products:
5+
- workers
6+
date: 2025-06-17T01:00:00Z
7+
---
8+
9+
import { WranglerConfig } from "~/components";
10+
11+
For those building [Single Page Applications (SPAs) on Workers](/workers/static-assets/routing/single-page-application/#advanced-routing-control), you can now explicitly define which routes invoke your Worker script in Wrangler configuration. The [`run_worker_first` config option](/workers/static-assets/binding/#run_worker_first) has now been expanded to accept an array of route patterns, allowing you to more granularly specify when your Worker script runs.
12+
13+
**Configuration example:**
14+
15+
<WranglerConfig>
16+
17+
```json
18+
{
19+
"name": "my-spa-worker",
20+
"compatibility_date": "$today",
21+
"main": "./src/index.ts",
22+
"assets": {
23+
"directory": "./dist/",
24+
"not_found_handling": "single-page-application",
25+
"binding": "ASSETS",
26+
"run_worker_first": ["/api/*", "!/api/docs/*"]
27+
}
28+
}
29+
```
30+
31+
</WranglerConfig>
32+
33+
This new routing control was done in partnership with our community and customers who provided great feedback on [our public proposal](https://github.com/cloudflare/workers-sdk/discussions/9143). Thank you to everyone who brought forward use-cases and feedback on the design!
34+
35+
### Prerequisites
36+
37+
To use advanced routing control with `run_worker_first`, you'll need:
38+
39+
- [Wrangler](/workers/wrangler/install-and-update/) v4.20.0 and above
40+
- [Cloudflare Vite plugin](/workers/vite-plugin/get-started/) v1.7.0 and above

src/content/docs/workers/static-assets/billing-and-limitations.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ description: Billing, troubleshooting, and limitations for Static assets on Work
1111

1212
Requests to a project with static assets can either return static assets or invoke the Worker script, depending on if the request [matches a static asset or not](/workers/static-assets/routing/).
1313

14-
Requests to static assets are free and unlimited. Requests to the Worker script (for example, in the case of SSR content) are billed according to Workers pricing. Refer to [pricing](/workers/platform/pricing/#example-2) for an example.
15-
16-
There is no additional cost for storing Assets.
14+
- Requests to static assets are free and unlimited. Requests to the Worker script (for example, in the case of SSR content) are billed according to Workers pricing. Refer to [pricing](/workers/platform/pricing/#example-2) for an example.
15+
- There is no additional cost for storing Assets.
16+
- **Important note for free tier users**: When using [`run_worker_first`](/workers/static-assets/binding/#run_worker_first), requests matching the specified patterns will always invoke your Worker script. If you exceed your free tier request limits, these requests will receive a 429 (Too Many Requests) response instead of falling back to static asset serving. Negative patterns (patterns beginning with `!/`) will continue to serve assets correctly, as requests are directed to assets, without invoking your Worker script.
1717

1818
## Limitations
1919

src/content/docs/workers/static-assets/binding.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,27 @@ run_worker_first = true
7979

8080
</WranglerConfig>
8181

82+
You can also specify `run_worker_first` as an array of route patterns to selectively run the Worker script first only for specific routes. This is often paired with the [`not_found_handling = "single-page-application"` setting](/workers/static-assets/routing/single-page-application/#advanced-routing-control):
83+
84+
<WranglerConfig>
85+
86+
```json
87+
{
88+
"name": "my-spa-worker",
89+
"compatibility_date": "$today",
90+
"main": "./src/index.ts",
91+
"assets": {
92+
"directory": "./dist/",
93+
"not_found_handling": "single-page-application",
94+
"binding": "ASSETS",
95+
"run_worker_first": ["/api/*", "!/api/docs/*"]
96+
}
97+
}
98+
```
99+
100+
</WranglerConfig>
101+
The array supports glob patterns with `*` for deep matching and exception patterns with `!` prefix. In this configuration, requests to `/api/*` routes will invoke the Worker script first, except for `/api/docs/*` which will follow the default asset-first routing behavior.
102+
82103
## `binding`
83104

84105
Configuring the optional [binding](/workers/runtime-apis/bindings) gives you access to the collection of assets from within your Worker script.

src/content/docs/workers/static-assets/index.mdx

Lines changed: 38 additions & 228 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import {
1616
Description,
1717
InlineBadge,
1818
Icon,
19-
DirectoryListing,
20-
FileTree,
2119
Render,
2220
TabItem,
2321
Tabs,
@@ -28,10 +26,26 @@ import {
2826
Flex,
2927
WranglerConfig,
3028
Steps,
29+
PackageManagers,
3130
} from "~/components";
3231

3332
You can upload static assets (HTML, CSS, images and other files) as part of your Worker, and Cloudflare will handle caching and serving them to web browsers.
3433

34+
**Start from CLI** - Scaffold a React SPA with an API Worker, and use the [Cloudflare Vite plugin](/workers/vite-plugin/).
35+
36+
<PackageManagers
37+
type="create"
38+
pkg="cloudflare@latest"
39+
args="my-react-app --framework=react"
40+
/>
41+
---
42+
43+
**Or just deploy to Cloudflare**
44+
45+
[![Deploy to Workers](https://deploy.workers.cloudflare.com/button)](https://dash.cloudflare.com/?to=/:account/workers-and-pages/create/deploy-to-workers&repository=https://github.com/cloudflare/templates/tree/main/vite-react-template)
46+
47+
Learn more about supported frameworks on Workers.
48+
3549
<LinkCard
3650
title="Supported frameworks"
3751
href="/workers/framework-guides/"
@@ -87,7 +101,7 @@ By default, if a requested URL matches a file in the static assets directory, th
87101

88102
The default behavior for requests which don't match a static asset can be changed by setting the [`not_found_handling` option under `assets`](/workers/wrangler/configuration/#assets) in your Wrangler configuration file:
89103

90-
- [`not_found_handling = "single-page-application"`](/workers/static-assets/routing/single-page-application/): Sets your application to return a `200 OK` response with `index.html` for requests which don't match a static asset. Use this if you have a Single Page Application.
104+
- [`not_found_handling = "single-page-application"`](/workers/static-assets/routing/single-page-application/): Sets your application to return a `200 OK` response with `index.html` for requests which don't match a static asset. Use this if you have a Single Page Application. We recommend pairing this with selective routing using `run_worker_first` for [advanced routing control](/workers/static-assets/routing/single-page-application/#advanced-routing-control).
91105
- [`not_found_handling = "404-page"`](/workers/static-assets/routing/static-site-generation/#custom-404-pages): Sets your application to return a `404 Not Found` response with the nearest `404.html` for requests which don't match a static asset.
92106

93107
<WranglerConfig>
@@ -101,16 +115,25 @@ The default behavior for requests which don't match a static asset can be change
101115

102116
</WranglerConfig>
103117

104-
If you want the Worker code to execute before serving an asset (for example, to protect an asset behind authentication), you can set `run_worker_first = true`.
118+
If you want the Worker code to execute before serving assets, you can use the `run_worker_first` option. This can be set to `true` to invoke the Worker script for all requests, or configured as an array of route patterns for selective Worker-script-first routing:
105119

106-
<WranglerConfig>
120+
**Invoking your Worker script on specific paths:**
107121

108-
```toml
109-
[assets]
110-
directory = "./dist"
111-
run_worker_first = true
122+
<WranglerConfig>
112123

113-
```
124+
```json
125+
{
126+
"name": "my-spa-worker",
127+
"compatibility_date": "$today",
128+
"main": "./src/index.ts",
129+
"assets": {
130+
"directory": "./dist/",
131+
"not_found_handling": "single-page-application",
132+
"binding": "ASSETS",
133+
"run_worker_first": ["/api/*", "!/api/docs/*"]
134+
}
135+
}
136+
```
114137

115138
</WranglerConfig>
116139

@@ -130,224 +153,11 @@ Cloudflare provides automatic caching for static assets across its network, ensu
130153

131154
## Try it out
132155

133-
#### 1. Create a new Worker project
134-
135-
```sh
136-
npm create cloudflare@latest -- my-dynamic-site
137-
```
138-
139-
**For setup, select the following options**:
140-
141-
- For _What would you like to start with?_, choose `Framework`.
142-
- For _Which framework would you like to use?_, choose `React`.
143-
- For _Which language do you want to use?_, choose `TypeScript`.
144-
- For _Do you want to use git for version control_?, choose `Yes`.
145-
- For _Do you want to deploy your application_?, choose `No` (we will be making some changes before deploying).
146-
147-
After setting up the project, change the directory by running the following command:
148-
149-
```sh
150-
cd my-dynamic-site
151-
```
152-
153-
#### 2. Build project
154-
155-
Run the following command to build the project:
156-
157-
```sh
158-
npm run build
159-
```
160-
161-
We should now see a new directory `/dist` in our project, which contains the compiled assets:
162-
163-
<FileTree>
164-
- package.json
165-
- index.html
166-
- ...
167-
- dist Asset directory
168-
- ... Compiled assets
169-
- src
170-
- ...
171-
- ...
172-
173-
</FileTree>
174-
175-
In the next step, we use a Wrangler configuration file to allow Cloudflare to locate our compiled assets.
176-
177-
#### 3. Add a Wrangler configuration file (`wrangler.toml` or `wrangler.json`)
178-
179-
<WranglerConfig>
180-
181-
```toml
182-
name = "my-spa"
183-
compatibility_date = "2025-01-01"
184-
[assets]
185-
directory = "./dist"
186-
```
187-
188-
</WranglerConfig>
189-
190-
**Notice the `[assets]` block**: here we have specified our directory where Cloudflare can find our compiled assets (`./dist`).
191-
192-
Our project structure should now look like this:
193-
194-
<FileTree>
195-
- package.json
196-
- index.html
197-
- **wrangler.toml** Wrangler configuration
198-
- ...
199-
- dist Asset directory
200-
- ... Compiled assets
201-
- src
202-
- ...
203-
- ...
204-
205-
</FileTree>
206-
207-
#### 4. Deploy with Wrangler
208-
209-
```sh
210-
npx wrangler deploy
211-
```
212-
213-
Our project is now deployed on Workers! But we can take this even further, by adding an **API Worker**.
214-
215-
#### 5. Adjust our Wrangler configuration
216-
217-
Replace the file contents of our Wrangler configuration with the following:
218-
219-
<WranglerConfig>
220-
221-
```toml
222-
name = "my-spa"
223-
main = "src/api/index.js"
224-
compatibility_date = "2025-01-01"
225-
[assets]
226-
directory = "./dist"
227-
binding = "ASSETS"
228-
not_found_handling = "single-page-application"
229-
230-
```
231-
232-
</WranglerConfig>
233-
234-
We have edited the Wrangler file in the following ways:
235-
236-
- Added `main = "src/api/index.js"` to tell Cloudflare where to find our Worker code.
237-
- Added an `ASSETS` binding, which our Worker code can use to fetch and serve assets.
238-
- Enabled routing for Single Page Applications, which ensures that unmatched routes (such as `/dashboard`) serve our `index.html`.
239-
240-
:::note
241-
242-
By default, Cloudflare serves a `404 Not Found` to unmatched routes. To have the frontend handle routing instead of the server, you must enable `not_found_handling = "single-page-application"`.
243-
244-
:::
245-
246-
#### 5. Create a new directory `/api`, and add an `index.js` file
247-
248-
Copy the contents below into the index.js file.
249-
250-
```js {13}
251-
// api/index.js
252-
253-
export default {
254-
async fetch(request, env) {
255-
const url = new URL(request.url);
256-
257-
if (url.pathname.startsWith("/api/")) {
258-
return new Response(JSON.stringify({ name: "Cloudflare" }), {
259-
headers: { "Content-Type": "application/json" },
260-
});
261-
}
262-
263-
return env.ASSETS.fetch(request);
264-
},
265-
};
266-
```
267-
268-
**Consider what this Worker does:**
269-
270-
- Our Worker receives a HTTP request and extracts the URL.
271-
- If the request is for an API route (`/api/...`), it returns a JSON response.
272-
- Otherwise, it serves static assets from our directory (`env.ASSETS`).
273-
274-
#### 6. Call the API from the client
275-
276-
Edit `src/App.tsx` so that it includes an additional button that calls the API, and sets some state. Replace the file contents with the following code:
277-
278-
```js {9,25, 33-47}
279-
280-
// src/App.tsx
281-
import { useState } from "react";
282-
import reactLogo from "./assets/react.svg";
283-
import viteLogo from "/vite.svg";
284-
import "./App.css";
285-
286-
function App() {
287-
const [count, setCount] = useState(0);
288-
const [name, setName] = useState("unknown");
289-
290-
return (
291-
<>
292-
<div>
293-
<a href="https://vite.dev" target="_blank">
294-
<img src={viteLogo} className="logo" alt="Vite logo" />
295-
</a>
296-
<a href="https://react.dev" target="_blank">
297-
<img src={reactLogo} className="logo react" alt="React logo" />
298-
</a>
299-
</div>
300-
<h1>Vite + React</h1>
301-
<div className="card">
302-
<button
303-
onClick={() => setCount((count) => count + 1)}
304-
aria-label="increment"
305-
>
306-
count is {count}
307-
</button>
308-
<p>
309-
Edit <code>src/App.tsx</code> and save to test HMR
310-
</p>
311-
</div>
312-
<div className="card">
313-
<button
314-
onClick={() => {
315-
fetch("/api/")
316-
.then((res) => res.json() as Promise<{ name: string }>)
317-
.then((data) => setName(data.name));
318-
}}
319-
aria-label="get name"
320-
>
321-
Name from API is: {name}
322-
</button>
323-
<p>
324-
Edit <code>api/index.ts</code> to change the name
325-
</p>
326-
</div>
327-
<p className="read-the-docs">
328-
Click on the Vite and React logos to learn more
329-
</p>
330-
</>
331-
);
332-
}
333-
334-
export default App;
335-
```
336-
337-
Before deploying again, we need to rebuild our project:
338-
339-
```sh
340-
npm run build
341-
```
342-
343-
#### 7. Deploy with Wrangler
344-
345-
```sh
346-
npx wrangler deploy
347-
348-
```
349-
350-
Now we can see a new button **Name from API**, and if you click the button, we can see our API response as **Cloudflare**!
156+
<LinkCard
157+
title="Vite + React SPA tutorial"
158+
href="/workers/vite-plugin/tutorial/"
159+
description="Learn how to build and deploy a full-stack Single Page Application with static assets and API routes."
160+
/>
351161

352162
## Learn more
353163

0 commit comments

Comments
 (0)