从独立 hostname 提供每个 sandbox 预览,例如 ada.example-previews.com。预览中的代码运行在访问者的浏览器中,每个预览 hostname 都是独立 origin。一个预览中的代码无法读取你的应用页面或另一个预览的存储。路径会原样到达 server,因此应用不需要 base path。
- .example-previews.com
- A wildcard DNS record and the route
*.example-previews.com/*send every preview hostname to your Worker. - ada
- Your Worker reads the sandbox name and calls
getByName("ada"). A name that is not a valid DNS label gets a404. - /app/
- The path reaches the web server in the container unchanged, with the preview hostname in the
Hostheader.
- Durable Object,其
fetch()handler 将请求转发到其 container 中的 Web server。要构建一个,请参阅 预览 Web 应用。 - Cloudflare 上用于预览的域名,例如
example-previews.com,并由 Cloudflare 管理其 DNS。请使用你的应用未使用的域名。
-
在预览域名的 DNS 设置中,添加已代理的 通配记录。例如,添加名称为
*、内容为100::的AAAA记录,并将代理状态设为 Proxied。该记录让 Cloudflare 接收每个预览 hostname 的请求。Worker 路由处理这些请求,因此 Cloudflare 永远不会联系记录中的地址。
-
在
wrangler.jsonc中,将预览域名添加为变量,并将其子域名路由到 Worker。将example-previews.com替换为你的预览域名:{ "vars": { "PREVIEW_DOMAIN": "example-previews.com", }, "routes": [ { "pattern": "*.example-previews.com/*", "zone_name": "example-previews.com", }, ], "workers_dev": true, }workers_dev = true [vars] PREVIEW_DOMAIN = "example-previews.com" [[routes]] pattern = "*.example-previews.com/*" zone_name = "example-previews.com"设置
routes时,Wrangler 会关闭workers.devURL。workers_dev: true会保留它,用于管理预览的请求。 -
为新变量生成类型:
npx wrangler typesyarn wrangler typespnpm wrangler types -
在 Worker 的
fetch()handler 中,将每个预览 hostname 发送到同名 Durable Object:src/index.jsjs export default { async fetch(request, env) { const url = new URL(request.url); const suffix = `.${env.PREVIEW_DOMAIN}`; // A preview hostname reaches only the sandbox with the same name. if (url.hostname.endsWith(suffix)) { const name = url.hostname.slice(0, -suffix.length); // Accept only sandbox names that are valid DNS labels if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(name)) { return new Response("Not found", { status: 404 }); } // Forward with the `http:` scheme, because `getTcpPort().fetch()` // does not accept `https:` URLs url.protocol = "http:"; return env.MY_CONTAINER.getByName(name).fetch(new Request(url, request)); } // Other hostnames manage previews. return new Response("Not found", { status: 404 }); }, };src/index.tsts export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); const suffix = `.${env.PREVIEW_DOMAIN}`; // A preview hostname reaches only the sandbox with the same name. if (url.hostname.endsWith(suffix)) { const name = url.hostname.slice(0, -suffix.length); // Accept only sandbox names that are valid DNS labels if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(name)) { return new Response("Not found", { status: 404 }); } // Forward with the `http:` scheme, because `getTcpPort().fetch()` // does not accept `https:` URLs url.protocol = "http:"; return env.MY_CONTAINER.getByName(name).fetch(new Request(url, request)); } // Other hostnames manage previews. return new Response("Not found", { status: 404 }); }, } satisfies ExportedHandler<Env>;server 会收到完整路径,以及
Hostheader 中的预览 hostname。发往预览 hostname 的每个请求都会进入预览。将管理预览的路由(例如停止预览)放在其他 hostname 上,以免预览中的代码从其自身 origin 调用它们。在预览 hostname 上认证预览访问者,不要将应用 session cookie 发送到那里。
-
部署你的 Worker:
npx wrangler deployyarn wrangler deploypnpm wrangler deploy -
在浏览器中打开名为
ada的预览,并在其浏览器控制台中存储一个值。将example-previews.com替换为你的预览域名:https://ada.example-previews.com/localStorage.setItem("name", "ada"); -
打开名为
grace的预览,并在其浏览器控制台中读取该值:https://grace.example-previews.com/localStorage.getItem("name");控制台会打印
null。每个预览 hostname 都是独立 origin,因此grace无法读取ada的存储。
要在你已使用的域名的子域名下提供预览(例如 previews.example.com),请按该子域名为通配记录和路由命名:
-
在
example.com的 DNS 设置中,添加名称为*.previews、内容为100::的已代理AAAA记录。名为*的 通配记录 在previews.example.com有自己的记录时不适用于其下方,并且还会匹配example.com的每个其他未使用子域名。名为*.previews的记录只匹配预览 hostname。 -
在
wrangler.jsonc中,将PREVIEW_DOMAIN设为该子域名,并将其子域名路由到 Worker:{ "vars": { "PREVIEW_DOMAIN": "previews.example.com", }, "routes": [ { "pattern": "*.previews.example.com/*", "zone_name": "example.com", }, ], "workers_dev": true, }workers_dev = true [vars] PREVIEW_DOMAIN = "previews.example.com" [[routes]] pattern = "*.previews.example.com/*" zone_name = "example.com"
应用也使用的域名下的预览会与应用共享 cookie。浏览器会将设置了 Domain=example.com 的 cookie 发送到每个子域名,包括 previews.example.com 下的每个预览。
一个预览中的代码可以设置 Domain=example-previews.com 的 cookie,然后每个其他预览都会收到该 cookie。在预览 hostname 上设置 cookie 时不要带 Domain 属性,也不要因为 cookie 到达预览 hostname 就信任它。
container 中的 server 使用 HTTPS:从 container 提供明文 HTTP。Cloudflare 处理预览 hostname 的 TLS,你的 Worker 通过 HTTP 将每个请求转发到 container。只接受 HTTPS 的 server(例如使用 Node.js 中的 https.createServer() 或框架的 HTTPS 标志启动的 server)无法处理这些请求。请改为用 HTTP 启动 server。
- 预览 Web 应用
- 预览工作区示例 ↗:可部署的 Worker,为每个 sandbox 在独立 hostname 上运行 Vite 开发 server。
- Sandbox 安全
- Routes