You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
Copy file name to clipboardExpand all lines: src/content/docs/workers/static-assets/billing-and-limitations.mdx
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,9 +11,9 @@ description: Billing, troubleshooting, and limitations for Static assets on Work
11
11
12
12
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/).
13
13
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.
Copy file name to clipboardExpand all lines: src/content/docs/workers/static-assets/binding.mdx
+21Lines changed: 21 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -79,6 +79,27 @@ run_worker_first = true
79
79
80
80
</WranglerConfig>
81
81
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
+
82
103
## `binding`
83
104
84
105
Configuring the optional [binding](/workers/runtime-apis/bindings) gives you access to the collection of assets from within your Worker script.
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.
34
33
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
+
[](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
+
35
49
<LinkCard
36
50
title="Supported frameworks"
37
51
href="/workers/framework-guides/"
@@ -87,7 +101,7 @@ By default, if a requested URL matches a file in the static assets directory, th
87
101
88
102
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:
89
103
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).
91
105
-[`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.
92
106
93
107
<WranglerConfig>
@@ -101,16 +115,25 @@ The default behavior for requests which don't match a static asset can be change
101
115
102
116
</WranglerConfig>
103
117
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:
105
119
106
-
<WranglerConfig>
120
+
**Invoking your Worker script on specific paths:**
107
121
108
-
```toml
109
-
[assets]
110
-
directory = "./dist"
111
-
run_worker_first = true
122
+
<WranglerConfig>
112
123
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
+
```
114
137
115
138
</WranglerConfig>
116
139
@@ -130,224 +153,11 @@ Cloudflare provides automatic caching for static assets across its network, ensu
130
153
131
154
## Try it out
132
155
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
- 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:
0 commit comments