{"_id":"@bunny.net/astro-adapter","name":"@bunny.net/astro-adapter","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@bunny.net/astro-adapter","version":"0.1.0","description":"Astro adapter for bunny.net Edge Scripting. Renders Astro on the edge and serves the build assets from Bunny Storage.","type":"module","license":"MIT","author":{"name":"bunny.net"},"homepage":"https://bunny.net/docs/scripting/frameworks/astro","repository":{"type":"git","url":"git+https://github.com/BunnyWay/bunny-adapters.git","directory":"packages/astro"},"bugs":{"url":"https://github.com/BunnyWay/bunny-adapters/issues"},"keywords":["astro-adapter","astro","astro-component","bunny","bunny.net","bunnycdn","edge-scripting","edge","ssr","withastro"],"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./server":{"types":"./dist/server.d.ts","default":"./dist/server.js"},"./image-service":{"types":"./dist/image-service.d.ts","default":"./dist/image-service.js"},"./session":{"types":"./dist/session.d.ts","default":"./dist/session.js"},"./cache":{"types":"./dist/cache.d.ts","default":"./dist/cache.js"},"./preview":{"types":"./dist/preview.d.ts","default":"./dist/preview.js"},"./local-zone":{"types":"./dist/build/local-zone.d.ts","default":"./dist/build/local-zone.js"},"./package.json":"./package.json"},"engines":{"node":">=22.12.0"},"scripts":{"build":"tsc -p tsconfig.json","check":"tsc -p tsconfig.json --noEmit","test":"node --test test/*.test.mjs","lint:package":"publint --strict && attw --pack . --profile esm-only","prepublishOnly":"npm run build"},"dependencies":{"@bunny.net/edgescript-sdk":"^0.12.1","esbuild":"^0.25.0"},"peerDependencies":{"astro":"^7.0.0"},"devDependencies":{"@arethetypeswrong/cli":"^0.18.0","@types/node":"^26.2.0","astro":"^7.2.3","publint":"^0.3.0","typescript":"^5.7.0"},"gitHead":"f6fc225344916e676d6011a862144ab3e51ee974","_id":"@bunny.net/astro-adapter@0.1.0","_nodeVersion":"22.23.2","_npmVersion":"11.19.0","dist":{"integrity":"sha512-yT4NcXw+RS1d9V8kBmJmgpS06K0mid+GyO6We0+Mxr0r4MhspQ5YI+MzNIX9GwsRawLhaZICLjuzTovhNKhSVQ==","shasum":"1bef0d7aa6f929b7b78eb6d9cfc2907ff9ca8e0a","tarball":"https://registry.npmjs.org/@bunny.net/astro-adapter/-/astro-adapter-0.1.0.tgz","fileCount":55,"unpackedSize":309200,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bunny.net%2fastro-adapter@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIG07l1UxikkUGLfqxjXdRDK4U58Il3SvFpyP6wMK56CeAiBJKONyTz293obBkhlxFlbUE0O+eYkJMQCUgEwmrUkY1A=="}]},"_npmUser":{"name":"bogdan-at-bunny","email":"bogdan@bunny.net"},"directories":{},"maintainers":[{"name":"kevin_at_bunny","email":"kevin@bunny.net"},{"name":"antho-bunny","email":"anthony@bunny.net"},{"name":"notrab","email":"jamie@notrab.dev"},{"name":"bogdan-at-bunny","email":"bogdan@bunny.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/astro-adapter_0.1.0_1787658351102_0.868698539284462"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-25T11:45:50.891Z","0.1.0":"2026-08-25T11:45:51.228Z","modified":"2026-08-25T11:45:51.730Z"},"maintainers":[{"name":"kevin_at_bunny","email":"kevin@bunny.net"},{"name":"antho-bunny","email":"anthony@bunny.net"},{"name":"notrab","email":"jamie@notrab.dev"},{"name":"bogdan-at-bunny","email":"bogdan@bunny.net"}],"description":"Astro adapter for bunny.net Edge Scripting. Renders Astro on the edge and serves the build assets from Bunny Storage.","homepage":"https://bunny.net/docs/scripting/frameworks/astro","keywords":["astro-adapter","astro","astro-component","bunny","bunny.net","bunnycdn","edge-scripting","edge","ssr","withastro"],"repository":{"type":"git","url":"git+https://github.com/BunnyWay/bunny-adapters.git","directory":"packages/astro"},"author":{"name":"bunny.net"},"bugs":{"url":"https://github.com/BunnyWay/bunny-adapters/issues"},"license":"MIT","readme":"# @bunny.net/astro-adapter\n\nRun [Astro](https://astro.build/) on\n[bunny.net Edge Scripting](https://bunny.net/docs/scripting).\n\n> ### Status: lab, work in progress\n>\n> **This is version 0.1.0, the first release.** The adapter's options, its build\n> output, and the `.bunny/build.json` it writes can all change in a minor\n> release. So pin the version you install, and read the changelog before you\n> raise it. Do not run this in production.\n\nAstro's server renders pages and runs API routes inside the Edge Script, per\nrequest, on a node near your visitor. The build's client assets and prerendered\npages are read from [Bunny Storage](https://bunny.net/docs/storage).\n\n```\nbrowser ──▶ pull zone ──▶ Edge Script (Astro SSR)\n                              │\n                              └─ assets and prerendered pages ─▶ Bunny Storage\n```\n\n**[See it running](https://astro-ssr-demo.bunny.run)** ·\n[source of that site](https://github.com/BunnyWay/bunny-adapters/tree/main/examples/astro-showcase)\n· [guide](https://bunny.net/docs/scripting/frameworks/astro)\n\n## Install\n\n```bash\nnpx astro add @bunny.net/astro-adapter\n```\n\nThat installs the adapter and writes the config. To do it yourself:\n\n```bash\nnpm install @bunny.net/astro-adapter\n```\n\nEither way, the config is the same:\n\n```js\n// astro.config.mjs\nimport { defineConfig } from \"astro/config\";\nimport bunny from \"@bunny.net/astro-adapter\";\n\nexport default defineConfig({\n  output: \"server\",\n  adapter: bunny(),\n});\n```\n\n## Deploy\n\n```bash\nbunny lab deploy astro\n```\n\nThat is the whole thing. The [bunny CLI](https://bunny.net/docs/cli) builds the\nsite, creates what it needs the first time, uploads the build, and publishes the\nscript:\n\n- a storage zone for the build's files and the site's sessions,\n- an Edge Script for Astro's server, with the pull zone that gives the site its\n  URL,\n- every variable the script reads, set from what the CLI already knows. No\n  password passes through your terminal.\n\nEach deploy goes to its own folder in the zone, and the build writes\n`.bunny/build.json` so the CLI knows what to send where. So a published release\ncan only read the files it was built against.\n\n```bash\nbunny lab deploy astro          # build and publish\nbunny lab undeploy astro        # delete the app and everything it runs on\n```\n\nThose two commands are the whole interface. `lab` says it is still being shaped,\nso expect to update a workflow built on it.\n\nThe CLI also applies the pull zone settings this adapter asks for, and says which\nones it changed: cookies pass through, Smart Cache goes off so the adapter's own\ncache headers count, and the zone's cache override goes off so they reach a\nvisitor unchanged.\n\n<details>\n<summary>Deploying by hand</summary>\n\nNothing stops you. Upload `dist/client` to a storage zone, deploy `dist/index.js`\nwith `bunny scripts deploy`, and set the variables in the next section yourself.\nBoth halves have to go out together: Astro renames its CSS and JavaScript\nbundles whenever they change, so a script deployed against last build's files\nloses its styles.\n\nSet `BUNNY_ASSET_PREFIX` when the files are in a folder rather than at the zone\nroot. `bunny lab deploy astro` sets it for you, in the bundle itself.\n\n</details>\n\n## Configure the script\n\n`bunny lab deploy astro` sets all of these. The table is here for a deploy you\nrun yourself, and for reading a script's settings in the dashboard.\n\n| Variable             | Purpose                                                          |\n| -------------------- | ---------------------------------------------------------------- |\n| `BUNNY_STORAGE_ZONE` | The zone holding `dist/client`                                   |\n| `BUNNY_STORAGE_HOST` | The zone's regional endpoint. Defaults to `storage.bunnycdn.com` |\n| `BUNNY_STORAGE_KEY`  | The zone's **read-only** password                                |\n| `BUNNY_ASSET_PREFIX` | The folder inside the zone that holds this deploy                |\n| `BUNNY_SESSION_ZONE` | Only for sessions. A zone the script may write to                |\n| `BUNNY_SESSION_KEY`  | Only for sessions. That zone's **write** password                |\n| `BUNNY_API_KEY`      | Only for cache purging                                           |\n| `BUNNY_PULLZONE_ID`  | Only for cache purging                                           |\n\nThe script only reads the asset zone, so it gets the read-only password, never\nthe one that can write or delete. No password is ever an adapter option: it\nwould end up in the bundle.\n\n```bash\nbunny scripts env set BUNNY_API_KEY <key> --secret\n```\n\n## Local development\n\n`astro dev` works as usual for day-to-day work.\n\n`astro preview` runs the file you are about to deploy. A local storage zone\nstands in for Bunny Storage, so assets, prerendered pages and sessions all work\nwith no account and no network:\n\n```bash\nnpm run build\nnpx astro preview\n```\n\nIt needs [Deno](https://deno.com/) 2, because Deno is the Edge Scripting\nruntime. The `cdn-` request headers only exist on the bunny.net network, so\nanything your code reads from them is absent locally.\n\nA [build with no server](#a-site-with-no-server) has no file to run, so\n`astro preview` serves `dist/client` from Astro's own static server. That needs\nno Deno.\n\n## What works\n\n| Astro feature                                                | Supported                                                |\n| ------------------------------------------------------------ | -------------------------------------------------------- |\n| Server-rendered pages                                        | Yes                                                      |\n| `src/pages/api/` endpoints                                   | Yes                                                      |\n| `src/middleware.ts`                                          | Yes                                                      |\n| Dynamic routes and server islands                            | Yes                                                      |\n| `Astro.request`, `Astro.locals`, `Astro.url`, `Astro.params` | Yes                                                      |\n| `Astro.clientAddress`                                        | Yes, from `x-forwarded-for`                              |\n| `Astro.cookies`, including `set()`                           | Yes, [see the note](#cookies)                            |\n| `Astro.session`                                              | Yes, stored in a storage zone                            |\n| `export const prerender = true`                              | Yes, served from Storage                                 |\n| Prerendered `404.astro` and `500.astro`                      | Yes, served from Storage                                 |\n| `routeRules` and cache purging                               | Yes. [Turn Smart Cache off](#turn-smart-cache-off-first) |\n| Image transformation                                         | [Not yet](#images). Optimizer cannot read a script       |\n| Range requests on a stored object                            | Yes, [see below](#large-files-and-range-requests)        |\n| `node:fs` and the other built-ins Deno provides              | Yes. [See below](#node-built-ins)                        |\n| `astro:env` secrets                                          | Yes, from script environment variables                   |\n| `astro preview`                                              | Yes. With Deno for a server build                        |\n| Static page headers, such as a CSP                           | Yes, [see below](#a-site-with-no-server)                 |\n| `sharp` image service                                        | No. Native binaries cannot run on the edge               |\n| Edge middleware as a separate function                       | No. Middleware runs inside the script                    |\n| i18n domains                                                 | Untested. Tell us if you need it                         |\n\n## A site with no server\n\nA build whose every route is prerendered deploys no script. Astro reports it as\na static build, the adapter hands that answer back, and `bunny sites deploy` uploads\n`dist/client` to a storage zone. The `bunny sites` router serves it, and nothing\nis invoked per request:\n\n```\n[@bunny.net/astro-adapter] Every route is prerendered, so this build deploys no\nscript: `bunny sites deploy` uploads dist/client, and the site is served as files.\n```\n\nFiles alone cannot answer a 404 with a page, send a redirect, or add a header.\nThe router does all three, for every framework, and it learns what to do from\nthree file names, the same three a static host reads:\n\n| The router reads | What it does with it                                        |\n| ---------------- | ----------------------------------------------------------- |\n| `404.html`       | Answers a path the build never produced, with status 404    |\n| `_redirects`     | Sends a redirect with a real status, not a `<meta>` refresh |\n| `_headers`       | Adds the headers Bunny Storage cannot hold, such as a CSP   |\n\n`404.html` is your own `404.astro`. The build writes the other two, from your\n`redirects` config and from the headers Astro asks the host to set, and it names\nthe hashed asset directory in `_headers` so those files can be cached forever:\n\n```\n# dist/client/_headers\n/_astro/*\n  Cache-Control: public, max-age=31536000, immutable\n/about\n  content-security-policy: script-src 'self' 'sha256-…'\n```\n\nAstro still writes its own meta-refresh page for each redirect, so a deploy that\nnever reaches the router sends the visitor on all the same. The rule in\n`_redirects` carries `!`, which is what makes the router's 301 win over it.\n\nOne case the routes cannot show: a prerendered page holding a `server:defer`\nisland still needs the script, and Astro reports such a project as a static\nbuild. Say so:\n\n```js\nadapter: bunny({ deploy: \"server\" }),\n```\n\n## Images\n\n> **Optimizer cannot read from an Edge Script yet.** With Optimizer on, every\n> image request that misses the CDN cache answers `523 Origin Connection\nFailed`. We measured this on two script-backed pull zones in August 2026, and\n> `npm run test:live -- --optimizer` reproduces it. The service below writes the\n> right URLs, and nothing serves them yet. Leave `imageService` at its default\n> until this is fixed.\n\nSet `imageService: \"bunny\"` to resize and re-encode with\n[Bunny Optimizer](https://bunny.net/docs/optimizer), at the edge, with no build\nstep and no `sharp`:\n\n```js\nbunny({\n  imageService: \"bunny\",\n  image: {\n    widths: [360, 720, 1080], // used to build a srcset\n    quality: 82,\n    maxWidth: 3840, // a crafted URL cannot ask for more\n  },\n});\n```\n\n```astro\n---\nimport { Image } from \"astro:assets\";\nimport hero from \"../assets/hero.png\";\n---\n\n<Image src={hero} widths={[360, 720, 1080]} alt=\"\" />\n```\n\nTurn Optimizer on for the pull zone first. It is a paid feature, so the adapter\nnever enables it for you, and the default stays `\"noop\"`. Without Optimizer the\nparameters are ignored and the original image is served.\n\nOptimizer only works on files your own pull zone serves, so an image on another\nhost passes through untouched.\n\n## Large files and range requests\n\nA stored object can be fetched in pieces. The script answers a `Range` request\nwith `206` and a `Content-Range`, and it says `Accept-Ranges: bytes` on every\nobject it serves out of Bunny Storage.\n\nThat header is the part that matters. A pull zone will not answer a range from\nits cache, and will not slice an object, unless the origin says it accepts\nranges. Without it a video is only seekable once it is fully cached, and a\nplayer has to download the whole file to skip ahead.\n\nFor a large file that is not cached yet, turn on **Optimize for large object\ndelivery** in the pull zone's caching settings. It fetches the object in chunks,\nso the first request is seekable too:\n\n```bash\nbunny api POST /pullzone/<pull-zone-id> --body '{\"EnableCacheSlice\": true}'\n```\n\nThe script also passes `If-None-Match` and `If-Modified-Since` through, so a\nbrowser and the pull zone both revalidate with a `304` instead of downloading\nthe object again.\n\n## Sessions\n\n`Astro.session` works out of the box. Each session is one object in a Bunny\nStorage zone, so every edge node reads the same value.\n\n`bunny lab deploy astro` sets this up: sessions go under `_sessions/` in the app's own\nstorage zone, which nothing serves, and only the session driver gets the\npassword that can write.\n\nFor a deploy you run yourself, give the script a zone it may write to, and keep\nthe asset zone read-only:\n\n```bash\nbunny scripts env set BUNNY_SESSION_ZONE my-site-sessions\nbunny scripts env set BUNNY_SESSION_KEY <write password> --secret\n```\n\nBunny Storage does not expire an object, so `session.ttl` controls the cookie\nand not the stored object. Delete old objects yourself if the zone grows.\n\nPass `sessions: false` to configure your own driver instead.\n\n## Caching and purging\n\nAstro's `routeRules` become the headers the bunny.net CDN reads:\n\n```js\nexport default defineConfig({\n  adapter: bunny(),\n  routeRules: {\n    \"/products/[...slug]\": { maxAge: 3600, swr: 600, tags: [\"products\"] },\n  },\n});\n```\n\n```\nCache-Control: public, max-age=0, must-revalidate, s-maxage=3600, stale-while-revalidate=600\nCDN-Tag: products,astro-path:/products/socks\n```\n\n`s-maxage` is for the CDN and `max-age` is for the browser. Splitting them is\nwhat makes a purge take effect straight away. Set\n`cache: { browserMaxAge: 30 }` if you would rather the browser cached too.\n\nPurge by tag, or by path:\n\n```ts\nawait Astro.cache.invalidate({ tags: [\"products\"] });\nawait Astro.cache.invalidate({ path: \"/products/socks\" });\n```\n\nThat calls the [purge API](https://bunny.net/docs/cdn/purge-cache), so the\nscript needs `BUNNY_API_KEY` and `BUNNY_PULLZONE_ID`. The CLI sets the pull zone\nid, and asks before it puts an account API key on a script. Pass `cache: false`\nto configure your own provider.\n\n### Turn Smart Cache off first\n\n[Smart Cache](https://bunny.net/docs/cdn/smart-cache) only caches known static\nfile extensions, and HTML is not one of them. It is on by default, and while it\nis on a `routeRules` entry has no effect: the page is rendered again for every\nrequest.\n\n`bunny lab deploy astro` turns it off, because the build manifest asks for that.\nFor a deploy you run yourself:\n\n```bash\nbunny api POST /pullzone/<pull-zone-id> --body '{\"EnableSmartCache\": false}'\n```\n\nSmart Cache exists to stop a misconfigured origin caching a personal page by\naccident. The adapter covers that another way: see the next section.\n\n### Why a dynamic page says no-store\n\nA bunny.net pull zone applies its own expiration, by default 30 days, to any\nresponse that carries no `Cache-Control`. With Smart Cache off, that would\ncache a page rendered for one visitor and hand it to the next one.\n\nSo the adapter sets `Cache-Control: private, no-store` on every server-rendered\nresponse that does not set one itself. A route with a `routeRules` entry, and a\nroute that sets its own header, both keep what they set. Change the default\nwith `serverCacheControl` if you know better for your site.\n\n## The edge context\n\nEvery page gets `Astro.locals.runtime`:\n\n```ts\nconst { country, requestId, clientAddress, waitUntil, caches, env } = Astro.locals.runtime;\n\n// Keep working after the response has gone out.\nwaitUntil(recordVisit(country));\n```\n\nAdd the types to your project:\n\n```ts\n// src/env.d.ts\ntype BunnyRuntime = import(\"@bunny.net/astro-adapter\").BunnyRuntime;\n\ndeclare namespace App {\n  interface Locals {\n    runtime: BunnyRuntime;\n  }\n}\n```\n\n## Node built-ins\n\nEdge Scripting provides most `node:` modules, so a dependency that imports one\nis usually fine. The adapter rewrites a bare `fs` to `node:fs` during the build,\nbecause the runtime only answers to the prefixed name.\n\n[`node:fs`](https://bunny.net/docs/scripting/node-fs) works over a virtual file\nsystem. Know what that is before you rely on it:\n\n- It starts empty on every cold start.\n- One isolate cannot see what another wrote.\n- What it holds counts against the script's memory.\n\nSo it is a scratch pad for one request, never a store. Anything that has to\noutlive a request belongs in Bunny Storage, which is where the adapter keeps\nassets and sessions. The showcase has a working endpoint at `/api/scratch`.\n\nWhat does not work is a package with a native binary. `sharp` is the usual one.\n\n## Cookies\n\nA pull zone created for a script has **Disable cookies** switched on, which\nstrips `Set-Cookie` from every response. The build manifest asks for it to be\noff, so `bunny lab deploy astro` turns it off and reports the change.\n\nFor a deploy you run yourself:\n\n```bash\nbunny api POST /pullzone/<pull-zone-id> --body '{\"DisableCookies\": false}'\n```\n\n## Options\n\nEvery option is optional. `bunny()` on its own is usually right.\n\n```js\nbunny({\n  // Storage\n  storageZone: \"my-site-assets\", // otherwise BUNNY_STORAGE_ZONE at runtime\n  storageHost: \"ny.storage.bunnycdn.com\", // otherwise BUNNY_STORAGE_HOST\n  assetCacheControl: \"public, max-age=31536000, immutable\",\n  pageCacheControl: \"public, max-age=60\",\n  serverCacheControl: \"private, no-store\", // for a page that sets none itself\n\n  // Features\n  imageService: \"noop\", // \"bunny\" for Optimizer, false to keep your own\n  image: {}, // settings for the Optimizer service\n  sessions: true, // false to configure your own driver\n  cache: true, // false to configure your own provider\n\n  // Build\n  deploy: \"auto\", // \"server\" to deploy the script even with every route prerendered\n  outfile: \"dist/index.js\",\n  bundle: true, // false to run your own bundler\n  assetManifest: true, // or a file count, above which the script probes instead\n  external: [], // modules esbuild must not bundle\n  sourcemap: false,\n  esbuild: (options) => options, // last resort\n});\n```\n\nThe storage password is never an option. It always comes from the environment,\nso it stays out of the bundle.\n\n### assetManifest\n\nThe adapter inlines the list of built client files into the script. A request\nfor something the build never produced then costs no lookup at all, and a\nprerendered page costs one instead of two. A script may only make 50\nsubrequests, so this matters on a busy page.\n\nAbove 20 000 files the list would cost more space than it saves, and the script\ngoes back to asking Storage. Pass a number to move that line, or `false` to\nswitch it off.\n\n## The build manifest\n\nThe build writes `.bunny/build.json`, which is how `bunny lab deploy astro` knows\nwhat to deploy without knowing anything about Astro:\n\n```jsonc\n{\n  \"manifestVersion\": 1,\n  \"adapter\": { \"package\": \"@bunny.net/astro-adapter\", \"version\": \"0.1.0\" },\n  \"framework\": { \"name\": \"astro\", \"version\": \"7.2.4\" },\n  \"kind\": \"ssr\",\n  \"script\": { \"entry\": \"dist/index.js\", \"type\": \"standalone\", \"bytes\": 668140 },\n  \"assets\": { \"dir\": \"dist/client\" },\n  \"requires\": {\n    \"pullZone\": { \"disableCookies\": false, \"enableSmartCache\": false },\n    \"storage\": { \"write\": true, \"reason\": \"Astro.session\" },\n    \"env\": [{ \"name\": \"BUNNY_STORAGE_ZONE\", \"reason\": \"the zone holding the client build\" }],\n  },\n}\n```\n\nA static build names no script, and asks for nothing else: the files are the\nwhole deploy.\n\n```jsonc\n{\n  \"manifestVersion\": 1,\n  \"adapter\": { \"package\": \"@bunny.net/astro-adapter\", \"version\": \"0.1.0\" },\n  \"framework\": { \"name\": \"astro\", \"version\": \"7.2.4\" },\n  \"kind\": \"static\",\n  \"assets\": { \"dir\": \"dist/client\" },\n  \"dev\": { \"command\": \"astro dev\", \"preview\": \"astro preview\" },\n}\n```\n\nIt is a build output, so keep it out of version control.\n[`docs/writing-an-adapter.md`](../../docs/writing-an-adapter.md) holds the\ncontract for the next adapter.\n\n## Limits\n\nEdge Scripting allows one JavaScript file of up to 10 MB, and gives a script\n500 ms to start. The showcase in this repository bundles to roughly 660 kB. See\nthe [Edge Scripting limits](https://bunny.net/docs/scripting/limits).\n\n## Troubleshooting\n\n**The bundle fails on a Node-only module.** Most `node:` built-ins work, so\ncheck which one it is. A package with a native binary never will. `sharp` is\nthe usual cause, and the adapter already replaces it. If another dependency\nneeds one, replace that dependency. See [Node built-ins](#node-built-ins).\n\n**`Astro.rewrite(\"/404\")` throws a 500.** A prerendered route has no server\ncomponent to rewrite to. Return `new Response(null, { status: 404 })` from the\npage instead, and the adapter serves your prerendered 404 page out of Storage.\nAn endpoint in `src/pages/api/` keeps its own 404, so a JSON client is never\nhanded a web page.\n\n**Prerendered pages 404.** The files and the script are out of step. Run\n`bunny lab deploy astro`, which sends both in one step: the files go up first, and\nthe deploy's own folder name is written into the bundle, so a release cannot read\nanother deploy's files.\n\n**A POST returns 403.** Astro checks the request origin for server output. This\nis Astro's CSRF protection. Adjust `security.checkOrigin` if you need to.\n\n**`Astro.cookies.set()` has no effect.** The pull zone is stripping the header.\nSee [Cookies](#cookies).\n\n**The build warns about `Astro.request.headers`.** Middleware also runs while\nAstro prerenders a page, where there is no live request. Guard it with\n`if (context.isPrerendered) return next();`.\n\n**A `routeRules` entry changes nothing.** Smart Cache is on, and it does not\ncache HTML. See [above](#turn-smart-cache-off-first).\n\n**A page shows another visitor's content.** Something removed the\n`Cache-Control` the adapter sets. Check that no edge rule overrides it, and\nthat the page is not setting a `public` directive itself.\n\n**Every request returns a storage error.** Check that `BUNNY_STORAGE_ZONE`\nmatches the zone name, that `BUNNY_STORAGE_KEY` is a password of that zone, and\nthat `BUNNY_STORAGE_HOST` matches its region. A zone created seconds ago can\nanswer `401` until it propagates.\n\n## Licence\n\n[MIT](./LICENSE)\n","readmeFilename":"README.md","_rev":"1-9b14dcc0bfe3113d541e8f21a61cff8a"}