Skip to content

Astro Troubleshooting

Run the command from the Astro project root. Astro 5 or newer is required.

Terminal window
npx @kenresoft-cms/create astro --update

The updater did not overwrite your edits. Merge manually, or use --force if you want the generated version. See Astro deployment.

Check:

  • the site uses server (on-demand) rendering and has a deployment adapter
  • PUBLIC_KENRESOFT_CMS_URL is set. A 503 with The CMS connection is not configured means the proxy had no URL at all
  • the site origin is in the CMS CORS_ORIGINS
  • the global_fetch_strictly_public compatibility flag, if both run on Cloudflare in one account
  • TRUSTED_PROXY_SECRET matches on both sides, if you rely on per-visitor rate limits

Calls through /cms return a 308 redirect and then a 404

Section titled “Calls through /cms return a 308 redirect and then a 404”

This happens on a site with trailingSlash: 'always' in astro.config, using @kenresoft-cms/astro 0.6.0 or older. Astro redirects /cms/api/v1/public/pages to /cms/api/v1/public/pages/ (a 308, even for POST), the old proxy forwarded that trailing slash, and the API returns 404 for a path that ends in one. Every browser call through the proxy fails, including sign-in.

Update the package, which drops the trailing slash before forwarding (0.6.1 or newer):

Terminal window
npx @kenresoft-cms/create astro --update

Sites that use Astro’s default trailingSlash are not affected.

To check, request the path without a slash and follow redirects. It should return JSON:

Terminal window
curl -L https://your-site.example/cms/api/v1/public/pages

A statically generated site fetches content at build time. Rebuild it, or switch to server rendering.

The token expires after 15 minutes, and the page must render on demand. See Live Preview.

Astro logs “custom src/fetch.ts does not call the actions()/middleware() handler”

Section titled “Astro logs “custom src/fetch.ts does not call the actions()/middleware() handler””

This comes from a src/fetch.ts in your own project, which Astro treats as a custom fetch handler. It is not produced by the CMS packages. Its default export must be an object with a fetch method that forwards to Astro’s default handler.

The installer says PUBLIC_KENRESOFT_CMS_URL is already set and kept the old value

Section titled “The installer says PUBLIC_KENRESOFT_CMS_URL is already set and kept the old value”

The installer never overwrites an existing value in .env or .env.example. If you passed a different --cms-url, it reports the difference under “Needs manual action” and leaves the file alone. Edit .env yourself if the existing value is wrong.

The value must be the origin of the CMS API Worker, not the Admin Worker. If the site talks to the admin origin, every request returns a 404 or an HTML page instead of JSON.

Every /cms call returns an empty 404 on Cloudflare

Section titled “Every /cms call returns an empty 404 on Cloudflare”

The site itself works, but every request through /cms, even /cms/api/v1/auth/get-session, returns 404 with an empty body, and wrangler tail shows:

TypeError: Cannot read properties of undefined (reading 'replace')
at createCmsProxy

The site was built without PUBLIC_KENRESOFT_CMS_URL, and its proxy file is from before @kenresoft-cms/create 0.3.3, which read the URL only at build time. Astro turns the crash into a bare 404. Run npx @kenresoft-cms/create astro --update so the proxy reads the Worker’s runtime variable first, make sure the Worker has PUBLIC_KENRESOFT_CMS_URL as a runtime variable, and deploy again. Setting the Build variable (see below) also fixes it.

It builds locally but the Cloudflare build fails

Section titled “It builds locally but the Cloudflare build fails”

The build log shows an error like this while prerendering a page such as /404:

TypeError: Cannot read properties of undefined (reading 'replace')
at createKenresoftClient
Failed to prerender https://your-site.example/404/

With @kenresoft-cms/astro 0.8.0 and later the message names the variable instead: createKenresoftClient: url is missing. Set PUBLIC_KENRESOFT_CMS_URL ....

.env is only read on your machine. Cloudflare’s Git builds don’t see it, so PUBLIC_KENRESOFT_CMS_URL is undefined and the CMS client fails when it starts. Add it as a plain Build variable (not a secret) in the Worker’s settings under Settings, Build, Variables and secrets. The value is your CMS API origin. Then retry the build.

To reproduce it locally, move .env aside and run the build.

Turnstile is on, but registering shows “Complete the human check” with no widget

Section titled “Turnstile is on, but registering shows “Complete the human check” with no widget”

TURNSTILE_SECRET_KEY is set on the API, which now requires a token on every sign-up, but the frontend has no PUBLIC_TURNSTILE_SITE_KEY, so it never renders the widget at all — there is no way to produce the token the API demands, and every sign-up fails. The two halves are set in different places and neither implies the other: the secret on the API doesn’t publish the site key anywhere a frontend can read it automatically.

Set PUBLIC_TURNSTILE_SITE_KEY to the widget’s site key. Like PUBLIC_KENRESOFT_CMS_URL, it’s read at build time, so on a Cloudflare Git build it must be a Build variable, not just a local .env value — see the entry above. It isn’t secret, so a plain variable is fine.

The Cloudflare build of a monorepo site also builds other apps

Section titled “The Cloudflare build of a monorepo site also builds other apps”

If your Astro site lives in a monorepo and the build log shows other apps building, or the script name is the repository root’s (> your-repo@x.y.z build), the build is running from the repository root instead of the site folder. The site builds fine, then an unrelated app fails, for example a CMS admin app that needs its own VITE_API_URL.

In the Worker’s settings under Settings, Build, set Root directory to the site folder (for example apps/website), then start a new build. Check the build’s own settings panel: it lists the root directory that build actually used. If the panel still shows / after you saved the value, change the field, save, change it back and save again, then retry.

Do not work around it by giving the site the other app’s variables. With the wrong root directory, the deploy command also runs from the repository root and can pick up the wrong Wrangler config.

Branch preview builds fail, but production builds work

Section titled “Branch preview builds fail, but production builds work”

In a monorepo where the repository root has the CMS’s own wrangler.toml and the site keeps its Wrangler config under another name (for example apps/website/wrangler.deploy.toml), Cloudflare’s preview builds for non-production branches fail while main builds deploy fine. The build log ends with:

Found both a user configuration file at "../../wrangler.toml"
and a deploy configuration file at ".wrangler/deploy/config.json".
But these do not share the same base path so it is not clear which should be used.

Preview builds run the Non-production branch deploy command, which defaults to npx wrangler versions upload with no --config. Wrangler then finds both the root wrangler.toml and the config Astro’s Cloudflare adapter generates. In the Worker’s settings under Settings, Build, set that command to name the site’s config, for example npx wrangler versions upload --config wrangler.deploy.toml, the same file your production deploy command uses. To check before pushing, run the command with --dry-run in the site folder after a build.

Requests between the site and the API fail with a routing error

Section titled “Requests between the site and the API fail with a routing error”

When the site and the CMS API run on the same Cloudflare account, the site’s server-side fetch to the API can fail unless the site’s Worker has this flag in wrangler.jsonc:

"compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"]

Keep the flags you already have and add this one. Redeploy the site afterwards.

Sign-in or forms fail through the proxy with an origin error

Section titled “Sign-in or forms fail through the proxy with an origin error”

The proxy forwards the browser’s Origin header on requests such as sign-in and form submissions. The API checks it against CORS_ORIGINS, so the site’s origin must be listed there even though the browser only talks to your own domain. Plain content reads do not send it, which is why a site can render pages correctly and still fail on the first sign-in. See Authentication and CORS.

The API sees the site’s Worker as the client until both sides share TRUSTED_PROXY_SECRET. Follow Preserve visitor IPs. To confirm both Workers have it:

Terminal window
wrangler secret list

Run it once in the site project and once in the CMS project. TRUSTED_PROXY_SECRET should appear in both. A secret change takes effect on its own, but if the value differs between the two, the API silently ignores the forwarded IP.

TypeScript: TRUSTED_PROXY_SECRET does not exist on type Env

Section titled “TypeScript: TRUSTED_PROXY_SECRET does not exist on type Env”

After you run wrangler types, astro check or your editor may report this in src/pages/cms/[...path].ts:

Property 'TRUSTED_PROXY_SECRET' does not exist on type 'Env'.

wrangler types builds Env from your Wrangler config, and secrets aren’t in that file. The code still works at runtime. To fix the type, declare the secret in src/env.d.ts:

declare namespace Cloudflare {
interface Env {
TRUSTED_PROXY_SECRET?: string;
}
}

Keep it optional, because the proxy works without it.