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
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.
Copy file name to clipboardExpand all lines: src/content/docs/workers/static-assets/binding.mdx
+26Lines changed: 26 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -63,6 +63,32 @@ Now Wrangler will not upload these files as client-side assets when deploying th
63
63
64
64
Controls whether to invoke the Worker script regardless of a request which would have otherwise matched an asset. `run_worker_first = false` (default) will serve any static asset matching a request, while `run_worker_first = true` will unconditionally [invoke your Worker script](/workers/static-assets/routing/worker-script/#run-your-worker-script-first).
65
65
66
+
You can also specify `run_worker_first` as an array of route patterns to selectively run the Worker first only for specific routes:
67
+
68
+
<WranglerConfig>
69
+
70
+
```toml title="wrangler.toml"
71
+
name = "my-worker"
72
+
compatibility_date = "2024-09-19"
73
+
main = "src/index.ts"
74
+
75
+
[assets]
76
+
directory = "./public/"
77
+
binding = "ASSETS"
78
+
run_worker_first = [
79
+
"/api/**", # API calls go to Worker first
80
+
"!/api/docs/**"# EXCEPTION: For /api/docs/**, try static assets first
81
+
]
82
+
```
83
+
84
+
</WranglerConfig>
85
+
86
+
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.
87
+
88
+
<Asidetype="caution">
89
+
When using `run_worker_first`, requests matching the specified patterns will always invoke your Worker script. If you're on the free tier and exceed your request limits, these requests will receive a 429 (Too Many Requests) response instead of falling back to static asset serving. Consider this when designing your routing patterns.
@@ -87,7 +87,7 @@ By default, if a requested URL matches a file in the static assets directory, th
87
87
88
88
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
89
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.
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. 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
91
-[`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
92
93
93
<WranglerConfig>
@@ -101,8 +101,9 @@ The default behavior for requests which don't match a static asset can be change
101
101
102
102
</WranglerConfig>
103
103
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`.
104
+
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 run the Worker for all requests, or configured as an array of route patterns for selective Worker-first routing:
105
105
106
+
**Run Worker for all requests:**
106
107
<WranglerConfig>
107
108
108
109
```toml
@@ -114,240 +115,45 @@ If you want the Worker code to execute before serving an asset (for example, to
114
115
115
116
</WranglerConfig>
116
117
117
-
<LinkCard
118
-
title="Routing options"
119
-
href="/workers/static-assets/routing/"
120
-
description="Learn more about how you can customize routing behavior."
121
-
/>
122
-
123
-
### Caching behavior
124
-
125
-
Cloudflare provides automatic caching for static assets across its network, ensuring fast delivery to users worldwide. When a static asset is requested, it is automatically cached for future requests.
126
-
127
-
-**First Request:** When an asset is requested for the first time, it is fetched from storage and cached at the nearest Cloudflare location.
128
-
129
-
-**Subsequent Requests:** If a request for the same asset reaches a data center that does not have it cached, Cloudflare's [tiered caching system](/cache/how-to/tiered-cache/) allows it to be retrieved from a nearby cache rather than going back to storage. This improves cache hit ratio, reduces latency, and reduces unnecessary origin fetches.
130
-
131
-
## Try it out
132
-
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
-
118
+
**Run Worker first for specific routes only:**
219
119
<WranglerConfig>
220
120
221
121
```toml
222
-
name = "my-spa"
223
-
main = "src/api/index.js"
224
-
compatibility_date = "2025-01-01"
225
122
[assets]
226
123
directory = "./dist"
227
-
binding = "ASSETS"
228
-
not_found_handling = "single-page-application"
124
+
run_worker_first = [
125
+
"/api/**", # API routes go to Worker first
126
+
"/auth/**", # Auth routes go to Worker first
127
+
"!/api/public/**"# Exception: public API docs served as assets
128
+
]
229
129
230
130
```
231
131
232
132
</WranglerConfig>
233
133
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"`.
134
+
This selective approach allows you to protect specific routes behind authentication while serving other assets directly, and disables automatic `Sec-Fetch-Mode: navigate` detection for explicit routing control.
243
135
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:
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
-
exportdefaultApp;
335
-
```
136
+
<LinkCard
137
+
title="Routing options"
138
+
href="/workers/static-assets/routing/"
139
+
description="Learn more about how you can customize routing behavior."
140
+
/>
336
141
337
-
Before deploying again, we need to rebuild our project:
142
+
### Caching behavior
338
143
339
-
```sh
340
-
npm run build
341
-
```
144
+
Cloudflare provides automatic caching for static assets across its network, ensuring fast delivery to users worldwide. When a static asset is requested, it is automatically cached for future requests.
342
145
343
-
#### 7. Deploy with Wrangler
146
+
-**First Request:** When an asset is requested for the first time, it is fetched from storage and cached at the nearest Cloudflare location.
344
147
345
-
```sh
346
-
npx wrangler deploy
148
+
-**Subsequent Requests:** If a request for the same asset reaches a data center that does not have it cached, Cloudflare's [tiered caching system](/cache/how-to/tiered-cache/) allows it to be retrieved from a nearby cache rather than going back to storage. This improves cache hit ratio, reduces latency, and reduces unnecessary origin fetches.
347
149
348
-
```
150
+
## Try it out
349
151
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**!
152
+
<LinkCard
153
+
title="Vite + React SPA tutorial"
154
+
href="/workers/vite-plugin/tutorial/"
155
+
description="Learn how to build and deploy a full-stack Single Page Application with static assets and API routes."
Copy file name to clipboardExpand all lines: src/content/docs/workers/static-assets/routing/single-page-application.mdx
+30Lines changed: 30 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -33,6 +33,36 @@ Configuring `assets.not_found_handling` to `single-page-application` overrides t
33
33
34
34
<Renderfile="navigation_requests" />
35
35
36
+
## Advanced routing control
37
+
38
+
For more explicit control over SPA routing behavior, you can use `run_worker_first` with an array of route patterns. This approach disables the automatic `Sec-Fetch-Mode: navigate` detection and gives you explicit control over which requests should be handled by your Worker script vs served as static assets.
39
+
40
+
<WranglerConfig>
41
+
42
+
```json
43
+
{
44
+
"name": "my-spa-worker",
45
+
"compatibility_date": "$today",
46
+
"main": "./src/index.ts",
47
+
"assets": {
48
+
"directory": "./dist/",
49
+
"binding": "ASSETS",
50
+
"run_worker_first": [
51
+
"/app/**", // App routes go to Worker for SPA handling
52
+
"!/app/assets/**"// Exception: static assets served directly
53
+
]
54
+
}
55
+
}
56
+
```
57
+
58
+
</WranglerConfig>
59
+
60
+
This configuration provides explicit routing control without relying on browser navigation headers, making it ideal for complex SPAs that need fine-grained routing behavior. Your Worker script will need to handle the matched routes and use the assets binding to serve the appropriate content.
61
+
62
+
<Asidetype="caution">
63
+
When using `run_worker_first`, requests matching the specified patterns will always invoke your Worker script. If you're on the free tier and exceed your request limits, these requests will receive a 429 (Too Many Requests) response instead of falling back to static asset serving. Consider this when designing your routing patterns.
64
+
</Aside>
65
+
36
66
## Local Development
37
67
38
68
If you are using a Vite-powered SPA framework, you might be interested in using our [Vite plugin](/workers/vite-plugin/) which offers a Vite-native developer experience.
0 commit comments