diff --git a/src/content/docs/workers/frameworks/framework-guides/angular.mdx b/src/content/docs/workers/frameworks/framework-guides/angular.mdx index 14dca8d2fcb607..27a1503ca9fdba 100644 --- a/src/content/docs/workers/frameworks/framework-guides/angular.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/angular.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Angular -head: [] +tags: ["full-stack"] description: Create an Angular application and deploy it to Cloudflare Workers with Workers Assets. --- diff --git a/src/content/docs/workers/frameworks/framework-guides/astro.mdx b/src/content/docs/workers/frameworks/framework-guides/astro.mdx index 3ec50366110920..4f0c9e1eb0531c 100644 --- a/src/content/docs/workers/frameworks/framework-guides/astro.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/astro.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Astro -head: [] +tags: ["full-stack"] description: Create an Astro application and deploy it to Cloudflare Workers with Workers Assets. --- diff --git a/src/content/docs/workers/frameworks/framework-guides/docusaurus.mdx b/src/content/docs/workers/frameworks/framework-guides/docusaurus.mdx index 699c9679f41d4d..48b65909599c77 100644 --- a/src/content/docs/workers/frameworks/framework-guides/docusaurus.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/docusaurus.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Docusaurus -head: [] +tags: ["ssg"] description: Create a Docusaurus application and deploy it to Cloudflare Workers with Workers Assets. --- diff --git a/src/content/docs/workers/frameworks/framework-guides/gatsby.mdx b/src/content/docs/workers/frameworks/framework-guides/gatsby.mdx index 37d1a6d1912628..88d357b7c79b47 100644 --- a/src/content/docs/workers/frameworks/framework-guides/gatsby.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/gatsby.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Gatsby -head: [] +tags: ["ssg"] description: Create a Gatsby application and deploy it to Cloudflare Workers with Workers Assets. --- diff --git a/src/content/docs/workers/frameworks/framework-guides/nextjs.mdx b/src/content/docs/workers/frameworks/framework-guides/nextjs.mdx index 9deec69b7cc237..96f3faa2500c73 100644 --- a/src/content/docs/workers/frameworks/framework-guides/nextjs.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/nextjs.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Next.js -head: [] +tags: ["full-stack"] description: Create an Next.js application and deploy it to Cloudflare Workers with Workers Assets. sidebar: badge: Beta diff --git a/src/content/docs/workers/frameworks/framework-guides/nuxt.mdx b/src/content/docs/workers/frameworks/framework-guides/nuxt.mdx index 8d3f2588b7475b..e593967667cb5a 100644 --- a/src/content/docs/workers/frameworks/framework-guides/nuxt.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/nuxt.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Nuxt -head: [] +tags: ["full-stack"] description: Create a Nuxt application and deploy it to Cloudflare Workers with Workers Assets. --- diff --git a/src/content/docs/workers/frameworks/framework-guides/qwik.mdx b/src/content/docs/workers/frameworks/framework-guides/qwik.mdx index a7fa96f3ba6bce..8df297c3f41c9f 100644 --- a/src/content/docs/workers/frameworks/framework-guides/qwik.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/qwik.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Qwik -head: [] +tags: ["full-stack"] description: Create a Qwik application and deploy it to Cloudflare Workers with Workers Assets. --- diff --git a/src/content/docs/workers/frameworks/framework-guides/react-router.mdx b/src/content/docs/workers/frameworks/framework-guides/react-router.mdx index ed54fb56d58342..025c84f75c381c 100644 --- a/src/content/docs/workers/frameworks/framework-guides/react-router.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/react-router.mdx @@ -1,6 +1,7 @@ --- pcx_content_type: how-to title: React Router (formerly Remix) +tags: ["full-stack"] description: Create a React Router application and deploy it to Cloudflare Workers --- diff --git a/src/content/docs/workers/frameworks/framework-guides/react.mdx b/src/content/docs/workers/frameworks/framework-guides/react.mdx index 0818f9da2d0e13..75f4698e42fb5c 100644 --- a/src/content/docs/workers/frameworks/framework-guides/react.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/react.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: React -head: [] +tags: ["spa"] description: Create a React application and deploy it to Cloudflare Workers with Workers Assets. --- diff --git a/src/content/docs/workers/frameworks/framework-guides/solid.mdx b/src/content/docs/workers/frameworks/framework-guides/solid.mdx index 68250af12f19d7..1cc833a80f20d3 100644 --- a/src/content/docs/workers/frameworks/framework-guides/solid.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/solid.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Solid -head: [] +tags: ["full-stack"] description: Create a Solid application and deploy it to Cloudflare Workers with Workers Assets. sidebar: badge: Beta diff --git a/src/content/docs/workers/frameworks/framework-guides/svelte.mdx b/src/content/docs/workers/frameworks/framework-guides/svelte.mdx index 60d442b13ff113..8f702510c1d589 100644 --- a/src/content/docs/workers/frameworks/framework-guides/svelte.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/svelte.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Svelte -head: [] +tags: ["spa"] description: Create a Svelte application and deploy it to Cloudflare Workers with Workers Assets. --- diff --git a/src/content/docs/workers/frameworks/framework-guides/vue.mdx b/src/content/docs/workers/frameworks/framework-guides/vue.mdx index 30ff09bf17b1a3..22502373709ef5 100644 --- a/src/content/docs/workers/frameworks/framework-guides/vue.mdx +++ b/src/content/docs/workers/frameworks/framework-guides/vue.mdx @@ -1,7 +1,7 @@ --- pcx_content_type: how-to title: Vue -head: [] +tags: ["spa"] description: Create a Vue application and deploy it to Cloudflare Workers with Workers Assets. --- diff --git a/src/content/docs/workers/static-assets/direct-upload.mdx b/src/content/docs/workers/static-assets/direct-upload.mdx index 1c1410cc1a025c..d82ff4b7673973 100644 --- a/src/content/docs/workers/static-assets/direct-upload.mdx +++ b/src/content/docs/workers/static-assets/direct-upload.mdx @@ -15,6 +15,7 @@ import { Render, TabItem, Tabs, + TypeScriptExample, } from "~/components"; import { Icon } from "astro-icon/components"; @@ -213,7 +214,7 @@ Optionally, an assets binding can be provided if you wish to fetch and serve ass ## Programmatic Example - + ```ts import * as fs from "fs"; @@ -427,4 +428,4 @@ if (buckets.length > 0) { await scriptUpload(completionToken); ``` - + diff --git a/src/content/docs/workers/static-assets/headers.mdx b/src/content/docs/workers/static-assets/headers.mdx index a918a253185029..217c22e8d4c239 100644 --- a/src/content/docs/workers/static-assets/headers.mdx +++ b/src/content/docs/workers/static-assets/headers.mdx @@ -23,6 +23,12 @@ When serving static assets, Workers will attach some headers to the response by This header complements the default `Cache-Control` header. Its value is a hash of the static asset file, and browsers can use this in subsequent requests with an `If-None-Match` header to check for freshness, without needing to re-download the entire file in the case of a match. +- **`CF-Cache-Status`** + + This header indicates whether the asset was served from the cache (`HIT`) or not (`MISS`).[^1] + Cloudflare reserves the right to attach new headers to static asset responses at any time in order to improve performance or harden the security of your Worker application. + +[^1]: Due to a technical limitation that we hope to address in the future, the `CF-Cache-Status` header is not always entirely accurate. It is possible for false-positives and false-negatives to occur. This should be rare. In the meantime, this header should be considered as returning a "probablistic" result. diff --git a/src/content/docs/workers/static-assets/routing.mdx b/src/content/docs/workers/static-assets/routing.mdx deleted file mode 100644 index dbba1b2e06a0c9..00000000000000 --- a/src/content/docs/workers/static-assets/routing.mdx +++ /dev/null @@ -1,324 +0,0 @@ ---- -pcx_content_type: concept -title: Routing -sidebar: - order: 3 -head: [] -description: How Workers assets' routing and its configuration works. ---- - -import { - Badge, - Description, - FileTree, - InlineBadge, - Render, - TabItem, - Tabs, - WranglerConfig, -} from "~/components"; - -## Default behavior - -By default, assets are served by attempting to match up the incoming request's pathname to a static asset. The structure and organization of files in your static asset directory, along with any routing configuration, determine the routing paths for your application. When a request invokes a Worker with assets: - -1. If a request is found with a matching path to the current route requested then that asset will always be served. - -In this example, request to `example.com/blog` serves the `blog.html` file. - -![A request to `example.com/blog` serves the `/blog.html` file. ](~/assets/images/workers/platform/assets/workers-assets-serve-asset.png) - -2. If there is no Worker script, [`not_found_handling`](/workers/static-assets/routing/#2-not_found_handling) will be evaluated. By default, a null-body 404-status response will be served. - -If a Worker is configured, and there are no assets that match the current route requested, the Worker will be invoked. The Worker can then "fall back" to `not_found_handling` asset behavior, by passing the incoming request through to the [asset binding](/workers/static-assets/binding/#runtime-api-reference). - -In this example, a request to `example.com/api` doesn't match a static asset so the Worker is invoked. - -![A request to `example.com/blog` runs the Worker.](~/assets/images/workers/platform/assets/workers-assets-invoke-worker.png) - -## Invoking Worker Script Ahead of Assets - -You may wish to run code before assets are served. This is often the case when implementing authentication, logging, personalization, internationalization, or other similar functions. [`run_worker_first`](/workers/static-assets/binding/#run_worker_first) is a configuration option available in [Wrangler configuration file](/workers/wrangler/configuration/) which controls this behavior. When enabled, `run_worker_first = true` will invoke your Worker's code, regardless of any assets that would have otherwise matched. - -Take the following directory structure, [Wrangler configuration file](/workers/wrangler/configuration/), and user Worker code: - - - -- wrangler.json -- package.json -- public - - supersecret.txt -- src - - index.ts - - - - - -```toml title="wrangler.toml" -name = "my-worker" -compatibility_date = "2024-09-19" -main = "src/index.ts" -assets = { directory = "./public/", binding = "ASSETS", run_worker_first = true } -``` - - - -```js -export default { - async fetch( - request: Request, - env: Env, - ctx: ExecutionContext - ): Promise { - const url = new URL(request.url); - if (url.pathname === "/supersecret.txt") { - const auth = request.headers.get("Authorization"); - if (!auth) { - return new Response("Forbidden", { - status: 403, - statusText: "Forbidden", - headers: { - "Content-Type": "text/plain", - }, - }); - } - } - - return await env.ASSETS.fetch(request); - }, -}; -``` - -In this example, any request will be routed to our user Worker, due to `run_worker_first` being enabled. As a result, any request being made `/supersecret.txt` without an `Authorization` header will result in a 403. - -## Routing configuration - -There are two options for asset serving that can be configured in the [Wrangler configuration file](/workers/wrangler/configuration/#assets): - -#### 1. `html_handling` - -Forcing or dropping trailing slashes on request paths (for example, `example.com/page/` vs. `example.com/page`) is often something that developers wish to control for cosmetic reasons. Additionally, it can impact SEO because search engines often treat URLs with and without trailing slashes as different, separate pages. This distinction can lead to duplicate content issues, indexing problems, and overall confusion about the correct canonical version of a page. - -`html_handling` configuration determines the redirects and rewrites of requests for HTML content. It is used to specify the pattern for canonical URLs, thus where Cloudflare serves HTML content from, and additionally, where Cloudflare redirects non-canonical URLs to. - -#### 2. `not_found_handling` - -In the event a request does not match a static asset, and there is no Worker script (or that Worker script calls `fetch()` on [the assets binding](/workers/static-assets/binding/)), `not_found_handling` determines how Cloudflare will construct a response. - -It can be used to serve single-page applications (SPAs), or to serve custom 404 HTML pages. - -If creating a SPA, place an `/index.html` in your asset directory. When `not_found_handling` is configured to `"single-page-application"`, this page will be served with a 200 response. - -If you have custom 404 HTML pages, and configure `not_found_handling` to `"404-page"`, Cloudflare will recursively navigate up by pathname segment to serve the nearest `404.html` file. For example, you can have a `/404.html` and `/blog/404.html` file, and Cloudflare will serve the `/blog/404.html` file for requests to `/blog/not-found` and the `/404.html` file for requests to `/foo/bar`. - -### Default configuration - -#### 1. `html_handling` - -`"auto-trailing-slash"` is the default mode if `html_handling` is not explicitly specified. - -Take the following directory structure: - - - -- file.html -- folder - - index.html - - - -Based on the incoming requests, the following assets would be served: - -| Incoming Request | Response | Asset Served | -| ------------------ | --------------- | ------------------ | -| /file | 200 | /file.html served | -| /file.html | 307 to /file | - | -| /file/ | 307 to /file | - | -| /file/index | 307 to /file | - | -| /file/index.html | 307 to /file | - | -| /folder | 307 to /folder/ | - | -| /folder.html | 307 to /folder/ | - | -| /folder/ | 200 | /folder/index.html | -| /folder/index | 307 to /folder/ | - | -| /folder/index.html | 307 to /folder/ | - | - -#### 2. `not_found_handling` - -`"none"` is the default mode if `not_found_handling` is not explicitly specified. - -For all non-matching requests, Cloudflare will return a null-body 404-status response. - -``` -/not-found -> 404 -/foo/path/doesnt/exist -> 404 -``` - -### Alternate configuration options - -Alternate configuration options are outlined on this page and can be specified in your project's [Wrangler configuration file](/workers/wrangler/configuration/#assets). If you're deploying using a [framework](/workers/frameworks/), these options will be defined by the framework provider. - -Example Wrangler configuration file: - - - -```toml title="wrangler.toml" -assets = { directory = "./public", binding = "ASSETS", html_handling = "force-trailing-slash", not_found_handling = "404-page" } -``` - - - -#### `html_handling = "auto-trailing-slash" | "force-trailing-slash" | "drop-trailing-slash" | "none"` - -Take the following directory structure: - - - -- file.html -- folder - - index.html - - - -**`html_handling: "auto-trailing-slash"`** - -Based on the incoming requests, the following assets would be served: - -| Incoming Request | Response | Asset Served | -| ------------------ | --------------- | ------------------ | -| /file | 200 | /file.html | -| /file.html | 307 to /file | - | -| /file/ | 307 to /file | - | -| /file/index | 307 to /file | - | -| /file/index.html | 307 to /file | - | -| /folder | 307 to /folder/ | - | -| /folder.html | 307 to /folder | - | -| /folder/ | 200 | /folder/index.html | -| /folder/index | 307 to /folder | - | -| /folder/index.html | 307 to /folder | - | - -**`html_handling: "force-trailing-slash"`** - -Based on the incoming requests, the following assets would be served: - -| Incoming Request | Response | Asset Served | -| ------------------ | --------------- | ------------------ | -| /file | 307 to /file/ | - | -| /file.html | 307 to /file/ | - | -| /file/ | 200 | /file.html | -| /file/index | 307 to /file/ | - | -| /file/index.html | 307 to /file/ | - | -| /folder | 307 to /folder/ | - | -| /folder.html | 307 to /folder/ | - | -| /folder/ | 200 | /folder/index.html | -| /folder/index | 307 to /folder/ | - | -| /folder/index.html | 307 to /folder/ | - | - -**`html_handling: "drop-trailing-slash"`** - -Based on the incoming requests, the following assets would be served: - -| Incoming Request | Response | Asset Served | -| ------------------ | -------------- | ------------------ | -| /file | 200 | /file.html | -| /file.html | 307 to /file | - | -| /file/ | 307 to /file | - | -| /file/index | 307 to /file | - | -| /file/index.html | 307 to /file | - | -| /folder | 200 | /folder/index.html | -| /folder.html | 307 to /folder | - | -| /folder/ | 307 to /folder | - | -| /folder/index | 307 to /folder | - | -| /folder/index.html | 307 to /folder | - | - -**`html_handling: "none"`** - -Based on the incoming requests, the following assets would be served: - -| Incoming Request | Response | Asset Served | -| ------------------ | ------------------------------- | ------------------------------- | -| /file | Depends on `not_found_handling` | Depends on `not_found_handling` | -| /file.html | 200 | /file.html | -| /file/ | Depends on `not_found_handling` | Depends on `not_found_handling` | -| /file/index | Depends on `not_found_handling` | Depends on `not_found_handling` | -| /file/index.html | Depends on `not_found_handling` | Depends on `not_found_handling` | -| /folder | Depends on `not_found_handling` | Depends on `not_found_handling` | -| /folder.html | Depends on `not_found_handling` | Depends on `not_found_handling` | -| /folder/ | Depends on `not_found_handling` | Depends on `not_found_handling` | -| /folder/index | Depends on `not_found_handling` | Depends on `not_found_handling` | -| /folder/index.html | 200 | /folder/index.html | - -#### `not_found_handling = "404-page" | "single-page-application" | "none"` - -Take the following directory structure: - - - -- 404.html -- index.html -- folder - - 404.html - - - -**`not_found_handling: "none"`** - -``` -/not-found -> 404 -/folder/doesnt/exist -> 404 -``` - -**`not_found_handling: "404-page"`** - -``` -/not-found -> 404 /404.html -/folder/doesnt/exist -> 404 /folder/404.html -``` - -**`not_found_handling: "single-page-application"`** - -``` -/not-found -> 200 /index.html -/folder/doesnt/exist -> 200 /index.html -``` - -## Serving assets from a custom path - -:::note -This feature requires Wrangler v3.98.0 or later. -::: - -Like with any other Worker, [you can configure a Worker with assets to run on a path of your domain](/workers/configuration/routing/routes/). -Assets defined for a Worker must be nested in a directory structure that mirrors the desired path. - -For example, to serve assets from `example.com/blog/*`, create a `blog` directory in your asset directory. - -{/* prettier-ignore */} - -- dist - - blog - - index.html - - posts - - post1.html - - post2.html - - -With a [Wrangler configuration file](/workers/wrangler/configuration/) like so: - - -```toml -name = "assets-on-a-path-example" -main = "src/index.js" -route = "example.com/blog/*" - -[assets] -directory = "dist" - -``` - - -In this example, requests to `example.com/blog/` will serve the `index.html` file, and requests to `example.com/blog/posts/post1` will serve the `post1.html` file. - -If you have a file outside the configured path, it will not be served. For example, if you have a `home.html` file in the root of your asset directory, it will not be served when requesting `example.com/blog/home`. -However, if needed, these files can still be manually fetched over [the binding](/workers/static-assets/binding/#binding). -``` diff --git a/src/content/docs/workers/static-assets/routing/advanced/html-handling.mdx b/src/content/docs/workers/static-assets/routing/advanced/html-handling.mdx new file mode 100644 index 00000000000000..b058cd572d237e --- /dev/null +++ b/src/content/docs/workers/static-assets/routing/advanced/html-handling.mdx @@ -0,0 +1,158 @@ +--- +pcx_content_type: concept +title: HTML handling +description: How to configure a HTML handling and trailing slashes for the static assets of your Worker. +--- + +import { FileTree, WranglerConfig } from "~/components"; + +Forcing or dropping trailing slashes on request paths (for example, `example.com/page/` vs. `example.com/page`) is often something that developers wish to control for cosmetic reasons. Additionally, it can impact SEO because search engines often treat URLs with and without trailing slashes as different, separate pages. This distinction can lead to duplicate content issues, indexing problems, and overall confusion about the correct canonical version of a page. + +The [`assets.html_handling` configuration](/workers/wrangler/configuration/#assets) determines the redirects and rewrites of requests for HTML content. It is used to specify the pattern for canonical URLs, thus where Cloudflare serves HTML content from, and additionally, where Cloudflare redirects non-canonical URLs to. + +Take the following directory structure: + + + +- dist + - file.html + - folder + - index.html + + + +## Automatic trailing slashes (default) + +This will usually give you the desired behavior automatically: individual files (e.g. `foo.html`) will be served _without_ a trailing flash and folder index files (e.g. `foo/index.html`) will be served _with_ a trailing slash. + + + +```json +{ + "name": "my-worker", + "compatibility_date": "$today", + "assets": { + "directory": "./dist/", + "html_handling": "auto-trailing-slash" + } +} +``` + + + +Based on the incoming requests, the following assets would be served: + +| Incoming Request | Response | Asset Served | +| ------------------ | --------------- | ----------------------- | +| /file | 200 | /dist/file.html | +| /file.html | 307 to /file | - | +| /file/ | 307 to /file | - | +| /file/index | 307 to /file | - | +| /file/index.html | 307 to /file | - | +| /folder | 307 to /folder/ | - | +| /folder.html | 307 to /folder | - | +| /folder/ | 200 | /dist/folder/index.html | +| /folder/index | 307 to /folder | - | +| /folder/index.html | 307 to /folder | - | + +## Force trailing slashes + +Alternatively, you can force trailing slashes (`force-trailing-slash`). + + + +```json +{ + "name": "my-worker", + "compatibility_date": "$today", + "assets": { + "directory": "./dist/", + "html_handling": "force-trailing-slash" + } +} +``` + + + +Based on the incoming requests, the following assets would be served: + +| Incoming Request | Response | Asset Served | +| ------------------ | --------------- | ----------------------- | +| /file | 307 to /file/ | - | +| /file.html | 307 to /file/ | - | +| /file/ | 200 | /dist/file.html | +| /file/index | 307 to /file/ | - | +| /file/index.html | 307 to /file/ | - | +| /folder | 307 to /folder/ | - | +| /folder.html | 307 to /folder/ | - | +| /folder/ | 200 | /dist/folder/index.html | +| /folder/index | 307 to /folder/ | - | +| /folder/index.html | 307 to /folder/ | - | + +## Drop trailing slashes + +Or you can drop trailing slashes (`drop-trailing-slash`). + + + +```json +{ + "name": "my-worker", + "compatibility_date": "$today", + "assets": { + "directory": "./dist/", + "html_handling": "drop-trailing-slash" + } +} +``` + + + +Based on the incoming requests, the following assets would be served: + +| Incoming Request | Response | Asset Served | +| ------------------ | -------------- | ----------------------- | +| /file | 200 | /dist/file.html | +| /file.html | 307 to /file | - | +| /file/ | 307 to /file | - | +| /file/index | 307 to /file | - | +| /file/index.html | 307 to /file | - | +| /folder | 200 | /dist/folder/index.html | +| /folder.html | 307 to /folder | - | +| /folder/ | 307 to /folder | - | +| /folder/index | 307 to /folder | - | +| /folder/index.html | 307 to /folder | - | + +## Disable HTML handling + +Alternatively, if you have bespoke needs, you can disable the built-in HTML handling entirely (`none`). + + + +```json +{ + "name": "my-worker", + "compatibility_date": "$today", + "assets": { + "directory": "./dist/", + "html_handling": "none" + } +} +``` + + + +Based on the incoming requests, the following assets would be served: + +| Incoming Request | Response | Asset Served | +| ------------------ | ------------------------------- | ------------------------------- | +| /file | Depends on `not_found_handling` | Depends on `not_found_handling` | +| /file.html | 200 | /dist/file.html | +| /file/ | Depends on `not_found_handling` | Depends on `not_found_handling` | +| /file/index | Depends on `not_found_handling` | Depends on `not_found_handling` | +| /file/index.html | Depends on `not_found_handling` | Depends on `not_found_handling` | +| /folder | Depends on `not_found_handling` | Depends on `not_found_handling` | +| /folder.html | Depends on `not_found_handling` | Depends on `not_found_handling` | +| /folder/ | Depends on `not_found_handling` | Depends on `not_found_handling` | +| /folder/index | Depends on `not_found_handling` | Depends on `not_found_handling` | +| /folder/index.html | 200 | /dist/folder/index.html | diff --git a/src/content/docs/workers/static-assets/routing/advanced/index.mdx b/src/content/docs/workers/static-assets/routing/advanced/index.mdx new file mode 100644 index 00000000000000..92e2d7ad7cbb82 --- /dev/null +++ b/src/content/docs/workers/static-assets/routing/advanced/index.mdx @@ -0,0 +1,14 @@ +--- +pcx_content_type: navigation +title: Advanced +sidebar: + order: 5 + group: + hideIndex: true +--- + +import { Badge, DirectoryListing } from "~/components"; + +Learn how to configure advanced routing options for the static assets of your Worker. + + diff --git a/src/content/docs/workers/static-assets/routing/advanced/serving-a-subdirectory.mdx b/src/content/docs/workers/static-assets/routing/advanced/serving-a-subdirectory.mdx new file mode 100644 index 00000000000000..35da3e1edbf7a4 --- /dev/null +++ b/src/content/docs/workers/static-assets/routing/advanced/serving-a-subdirectory.mdx @@ -0,0 +1,46 @@ +--- +pcx_content_type: concept +title: Serving a subdirectory +description: How to configure a Worker with static assets on a subpath. +--- + +import { FileTree, WranglerConfig } from "~/components"; + +:::note +This feature requires Wrangler v3.98.0 or later. +::: + +Like with any other Worker, [you can configure a Worker with assets to run on a path of your domain](/workers/configuration/routing/routes/). +Assets defined for a Worker must be nested in a directory structure that mirrors the desired path. + +For example, to serve assets from `example.com/blog/*`, create a `blog` directory in your asset directory. + + + +- dist + - blog + - index.html + - posts + - post1.html + - post2.html + + + +With a [Wrangler configuration file](/workers/wrangler/configuration/) like so: + + + +```toml +name = "assets-on-a-path-example" +main = "src/index.js" +route = "example.com/blog/*" + +[assets] +directory = "dist" +``` + + + +In this example, requests to `example.com/blog/` will serve the `index.html` file, and requests to `example.com/blog/posts/post1` will serve the `post1.html` file. + +If you have a file outside the configured path, it will not be served, unless it is part of the `assets.not_found_handling` for [Single Page Applications](/workers/static-assets/routing/single-page-application/) or [custom 404 pages](/workers/static-assets/routing/static-site-generation/). For example, if you have a `home.html` file in the root of your asset directory, it will not be served when requesting `example.com/blog/home`. However, if needed, these files can still be manually fetched over [the binding](/workers/static-assets/binding/#binding). diff --git a/src/content/docs/workers/static-assets/routing/full-stack-application.mdx b/src/content/docs/workers/static-assets/routing/full-stack-application.mdx new file mode 100644 index 00000000000000..ccdef894161c3e --- /dev/null +++ b/src/content/docs/workers/static-assets/routing/full-stack-application.mdx @@ -0,0 +1,18 @@ +--- +pcx_content_type: concept +title: Full-stack application +description: How to configure and use a full-stack application with Workers. +sidebar: + order: 1 +--- + +import { WranglerConfig, Render, DirectoryListing } from "~/components"; + +Full-stack applications are web applications which are span both the client and server. The build process of these applications will produce a HTML files, accompanying client-side resources (e.g. JavaScript bundles, CSS stylesheets, images, fonts, etc.) and a Worker script. Data is typically fetched the Worker script at request-time and the initial page response is usually server-side rendered (SSR). From there, the client is then hydrated and a SPA-like experience ensues. + +The following full-stack frameworks are natively supported by Workers: + + diff --git a/src/content/docs/workers/static-assets/routing/index.mdx b/src/content/docs/workers/static-assets/routing/index.mdx new file mode 100644 index 00000000000000..35a3c9de909471 --- /dev/null +++ b/src/content/docs/workers/static-assets/routing/index.mdx @@ -0,0 +1,14 @@ +--- +pcx_content_type: navigation +title: Routing +sidebar: + order: 3 + group: + hideIndex: true +--- + +import { Badge, DirectoryListing } from "~/components"; + +Learn how to configure different archtectures for the static assets of your Worker. + + diff --git a/src/content/docs/workers/static-assets/routing/server-side-generated.mdx b/src/content/docs/workers/static-assets/routing/server-side-generated.mdx new file mode 100644 index 00000000000000..a6a2ae8dcffce0 --- /dev/null +++ b/src/content/docs/workers/static-assets/routing/server-side-generated.mdx @@ -0,0 +1,48 @@ +--- +pcx_content_type: concept +title: Static-Site Generation (SSG) application +description: How to configure and use a Static Site Generation (SSG) application with Workers. +sidebar: + order: 3 +--- + +import { WranglerConfig, Render } from "~/components"; + +Static Site Generation (SSG) applications are web applications which are predominantely built or "prerendered" ahead-of-time. They are often built with a framework such as [Gatsby](/workers/frameworks/framework-guides/gatsby/) or [Docusaurus](/workers/frameworks/framework-guides/docusaurus/). The build process of these frameworks will produce a many HTML files and accompanying client-side resources (e.g. JavaScript bundles, CSS stylesheets, images, fonts, etc.). Data is either static, fetched and compiled into the HTML at build-time, or fetched by the client from an API with client-side requests. + +Often, an SSG framework will allow you to create a custom 404 page. + +## Configuration + +In order to deploy a Static Site Generation application to Workers, you must configure the `assets.directory`, and optionally, the `assets.not_found_handling` and `assets.html_handling` options in your [Wrangler configuration file](/workers/wrangler/configuration/#assets): + + + +```json +{ + "name": "my-worker", + "compatibility_date": "$today", + "assets": { + "directory": "./dist/", + "not_found_handling": "404-page", + "html_handling": "auto-trailing-slash" + } +} +``` + + + +Configuring `assets.not_found_handling` to `404-page` overrides the default serving behavior of Workers for static assets. When an incoming request does not match a file in the `assets.directory`, Workers will serve the contents of the nearest `404.html` file with a `404 Not Found` status. + +`assets.html_handling` defaults to `auto-trailing-slash` and this will usually give you the desired behavior automatically: individual files (e.g. `foo.html`) will be served _without_ a trailing flash and folder index files (e.g. `foo/index.html`) will be served _with_ a trailing slash. Alternatively, you can force trailing slashes (`force-trailing-slash`) or drop trailing slashes (`drop-trailing-slash`) on requests for HTML pages. + + + +## Local Development + +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. + + diff --git a/src/content/docs/workers/static-assets/routing/single-page-application.mdx b/src/content/docs/workers/static-assets/routing/single-page-application.mdx new file mode 100644 index 00000000000000..f609df5588c2c5 --- /dev/null +++ b/src/content/docs/workers/static-assets/routing/single-page-application.mdx @@ -0,0 +1,40 @@ +--- +pcx_content_type: concept +title: Single Page Application (SPA) +description: How to configure and use a Single Page Application (SPA) with Workers. +sidebar: + order: 2 +--- + +import { WranglerConfig, Aside, TypeScriptExample, Render } from "~/components"; + +Single Page Applications (SPAs) are web applications which are client-side rendered (CSR). They are often built with a framework such as [React](/workers/frameworks/framework-guides/react/), [Vue](/workers/frameworks/framework-guides/vue/) or [Svelte](/workers/frameworks/framework-guides/svelte/). The build process of these frameworks will produce a single `/index.html` file and accompanying client-side resources (e.g. JavaScript bundles, CSS stylesheets, images, fonts, etc.). Typically, data is fetched by the client from an API with client-side requests. + +## Configuration + +In order to deploy a Single Page Application to Workers, you must configure the `assets.directory` and `assets.not_found_handling` options in your [Wrangler configuration file](/workers/wrangler/configuration/#assets): + + + +```json +{ + "name": "my-worker", + "compatibility_date": "$today", + "assets": { + "directory": "./dist/", + "not_found_handling": "single-page-application" + } +} +``` + + + +Configuring `assets.not_found_handling` to `single-page-application` overrides the default serving behavior of Workers for static assets. When an incoming request does not match a file in the `assets.directory`, Workers will serve the contents of the `/index.html` file with a `200 OK` status. + + + +## Local Development + +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. + + \ No newline at end of file diff --git a/src/content/docs/workers/static-assets/routing/worker-script.mdx b/src/content/docs/workers/static-assets/routing/worker-script.mdx new file mode 100644 index 00000000000000..65d4c5c775bc0d --- /dev/null +++ b/src/content/docs/workers/static-assets/routing/worker-script.mdx @@ -0,0 +1,78 @@ +--- +pcx_content_type: concept +title: Worker script +description: How the presence of a Worker script influences static asset routing and the related configuration options. +sidebar: + order: 4 +--- + +import { WranglerConfig, TypeScriptExample, Aside } from "~/components"; + +If you have both static assets and a Worker script configured, Cloudflare will first attempt to serve static assets if one matches the incoming request. You can read more about how we match assets in the [HTML handling docs](/workers/static-assets/routing/html-handling/). + +If an appropriate static asset if not found, Cloudflare will invoke your Worker script. + +This allows you to easily combine together these two features to create powerful applications (e.g. a [full-stack application](/workers/static-assets/routing/full-stack-application/), or a [Single Page Application (SPA)](/workers/static-assets/routing/single-page-application/) or [Static Site Generation (SSG) application](/workers/static-assets/routing/server-side-generated/) with an API). + +## Run your Worker script first + +If you need to always run your Worker script before serving static assets, (for example, you wish to log requests, perform some authentication checks, use [HTMLRewriter](/workers/runtime-apis/html-rewriter/), or otherwise transform assets before serving), you can configure the [`assets.run_worker_first` setting](/workers/static-assets/binding/#run_worker_first). This will retain any other settings governing asset-serving behavior (e.g. `assets.not_found_handling`) but gives you more control over exactly how and when those assets are served. This could be considered a platform-level "middleware" feature. + + + +Once the Worker has been invoked, to then defer to static-asset serving, you can use an Assets binding: + + + +```json +{ + "name": "my-worker", + "compatibility_date": "$today", + "main": "./worker/index.ts", + "assets": { + "directory": "./dist/", + "binding": "ASSETS", + "run_worker_first": true + } +} +``` + + + + + +```ts +import { WorkerEntrypoint } from "cloudflare:workers"; + +export default class extends WorkerEntrypoint { + async fetch(request: Request) { + // You can perform checks before fetching assets + const user = await checkIfRequestIsAuthenticated(request); + + if (!user) { + return new Response("Unauthorized", { status: 401 }); + } + + // You can then just fetch the assets as normal, or you could pass in a custom Request object here if you wanted to fetch some other specific asset + const assetResponse = await this.env.ASSETS.fetch(request); + + // You can return static asset response as-is, or you can transform them with something like HTMLRewriter + return new HTMLRewriter() + .on("#user", { + element(element) { + element.setInnerContent(JSON.stringify({ name: user.name })); + }, + }) + .transform(assetResponse); + } +} +``` + + diff --git a/src/content/partials/workers/custom_headers.mdx b/src/content/partials/workers/custom_headers.mdx index c3dac110d6f130..ff960f6ef8c9c9 100644 --- a/src/content/partials/workers/custom_headers.mdx +++ b/src/content/partials/workers/custom_headers.mdx @@ -8,7 +8,7 @@ import { Markdown, Render } from "~/components"; ## Custom headers -The default response headers served on static asset responses can be overridden, or added to, by creating a plain text file called `_headers` without a file extension, in the static asset directory of your project. This file will not itself be served as a static asset, but will instead be parsed by {props.product === 'workers' ? 'Workers' : 'Cloudflare Pages'} and its rules will be applied to static asset responses. +The default response headers served on static asset responses can be overridden, removed, or added to, by creating a plain text file called `_headers` without a file extension, in the static asset directory of your project. This file will not itself be served as a static asset, but will instead be parsed by {props.product === 'workers' ? 'Workers' : 'Cloudflare Pages'} and its rules will be applied to static asset responses. {props.filename} file can go directly into your {props.product === 'workers' ? static assets directory : build output directory}. +If you are using a framework, you will often have a directory named `public/` or `static/`, and this usually contains deploy-ready assets, such as favicons, `robots.txt` files, and site manifests. These files get copied over to a final output directory during the build, so this is the perfect place to author your {props.filename} file. If you are not using a framework, the {props.filename} file can go directly into your {props.product === 'workers' ? static assets directory : build output directory}. diff --git a/src/content/partials/workers/navigation_requests.mdx b/src/content/partials/workers/navigation_requests.mdx new file mode 100644 index 00000000000000..4f68c52e90fde5 --- /dev/null +++ b/src/content/partials/workers/navigation_requests.mdx @@ -0,0 +1,77 @@ +--- +{} +--- + +import { Aside, TypeScriptExample } from "~/components"; + +### Navigation requests + +If you have a Worker script (`main`), have configured `assets.not_found_handling`, and use the [`assets_navigation_prefers_asset_serving` compatibility flag](/workers/configuration/compatibility-flags/#navigation-requests-prefer-asset-serving) (or set a compatibility date of `2025-04-01` or greater), _navigation requests_ will not invoke the Worker script. A _navigation request_ is a request made with the `Sec-Fetch-Mode: navigate` header, which browsers automatically attach when navigating to a page. This reduces billable invocations of your Worker script, and is particularly useful for client-heavy applications which would otherwise invoke your Worker script very frequently and unnecessarily. + + + + + +#### Client-side callbacks + +In some cases, you might need to pass a value from a navigation request to your Worker script. For example, if you are acting as an OAuth callback, you might expect to see requests made to some route such as `/oauth/callback?code=...`. With the `assets_navigation_prefers_asset_serving` flag, your HTML assets will be server, rather than your Worker script. In this case, we recommend, either as part of your client application for this appropriate route, or with a slimmed-down endpoint-specific HTML file, passing the value to the server with client-side JavaScript. + +```html title="./dist/oauth/callback.html" + + + + OAuth callback + + +

Loading...

+ + + +``` + + + +```ts +import { WorkerEntrypoint } from "cloudflare:workers"; + +export default class extends WorkerEntrypoint { + async fetch(request: Request) { + const url = new URL(request.url); + if (url.pathname === "/api/oauth/callback") { + const code = url.searchParams.get("code"); + + const sessionId = await exchangeAuthorizationCodeForAccessAndRefreshTokensAndPersistToDatabaseAndGetSessionId(code); + + if (sessionId) { + return new Response(null, { + headers: { + "Set-Cookie": `sessionId=${sessionId}; HttpOnly; SameSite=Strict; Secure; Path=/; Max-Age=86400`, + }, + }); + } else { + return Response.json( + { error: "Invalid OAuth code. Please try again." }, + { status: 400 } + ); + } + } + + return new Response(null, { status: 404 }); + } +} +``` + + \ No newline at end of file diff --git a/src/content/partials/workers/static_asset_routing_reference.mdx b/src/content/partials/workers/static_asset_routing_reference.mdx new file mode 100644 index 00000000000000..6d6a236b560231 --- /dev/null +++ b/src/content/partials/workers/static_asset_routing_reference.mdx @@ -0,0 +1,64 @@ +--- +params: + - not_found_handling +--- + +### Reference + +In most cases, configuring `assets.not_found_handling` to {props.not_found_handling} will provide the desired behavior. If you are building your own framework, or have specialized needs, the following diagram can provide insight into exactly how the routing decisions are made. + +
+Full routing decision diagram +
+{`flowchart
+	Request@{ shape: stadium, label: "Incoming request" }
+	Request-->RunWorkerFirst
+	RunWorkerFirst@{ shape: diamond, label: "Run Worker script first?" }
+	RunWorkerFirst-->|No|RequestMatchesAsset
+	RunWorkerFirst-->|Yes|WorkerScriptInvoked
+	RequestMatchesAsset@{ shape: diamond, label: "Request matches asset?" }
+	RequestMatchesAsset-->|Yes|AssetServing
+	RequestMatchesAsset-->|No|WorkerScriptPresent
+	WorkerScriptPresent@{ shape: diamond, label: "Worker script present?" }
+	WorkerScriptPresent-->|No|AssetServing
+	WorkerScriptPresent-->|Yes|RequestNavigation
+	RequestNavigation@{ shape: diamond, label: "Request is navigation request?" }
+	RequestNavigation-->|No|WorkerScriptInvoked
+	WorkerScriptInvoked@{ shape: rect, label: "Worker script invoked" }
+	WorkerScriptInvoked-.->|Asset binding|AssetServing
+	RequestNavigation-->|Yes|AssetServing
+
+	subgraph Asset serving
+		AssetServing@{ shape: diamond, label: "Request matches asset?" }
+		AssetServing-->|Yes|AssetServed
+		AssetServed@{ shape: stadium, label: "**200 OK**
asset served" } + AssetServing-->|No|NotFoundHandling + + ${props.not_found_handling === "single-page-application" ? `subgraph single-page-application + NotFoundHandling@{ shape: rect, label: "Request rewritten to /index.html" } + NotFoundHandling-->SPAExists + SPAExists@{ shape: diamond, label: "HTML Page exists?" } + SPAExists-->|Yes|SPAServed + SPAExists-->|No|Generic404PageServed + Generic404PageServed@{ shape: stadium, label: "**404 Not Found**
null-body response served" } + SPAServed@{ shape: stadium, label: "**200 OK**
/index.html page served" } + end` : `subgraph 404-page + NotFoundHandling@{ shape: rect, label: "Request rewritten to ../404.html" } + NotFoundHandling-->404PageExists + 404PageExists@{ shape: diamond, label: "HTML Page exists?" } + 404PageExists-->|Yes|404PageServed + 404PageExists-->|No|404PageAtIndex + 404PageAtIndex@{ shape: diamond, label: "Request is for root /404.html?" } + 404PageAtIndex-->|Yes|Generic404PageServed + 404PageAtIndex-->|No|NotFoundHandling + Generic404PageServed@{ shape: stadium, label: "**404 Not Found**
null-body response served" } + 404PageServed@{ shape: stadium, label: "**404 Not Found**
404.html page served" } + end`} + + end`} +
+
+ +Requests are only billable if a Worker script is invoked. From there, it is possible to serve assets using the assets binding (depicted as the dotted line in the diagram above). + +{props.not_found_handling === "single-page-application" ? "Although unlikely to impact how a SPA is served, you" : "You"} can read more about how we match assets in the [HTML handling docs](/workers/static-assets/routing/html-handling/).