Introduction
According to Wikipedia:
Cloudflare, Inc. is an American company that provides a content delivery network, Internet security services, and distributed domain name server services, sitting between the visitor and the Cloudflare user’s hosting provider, acting as a reverse proxy for websites.
Workers is based on a lightweight execution engine built on Node.js (not Node.js directly). It is much faster and more practical than using a FaaS on Aliyun FC or Amazon Lambda to deploy applications like this blog built with Astro, although each technology has its own advantages and disadvantages. One advantage of Workers is the speed of its execution. Another is its cost.
This guide shows how to deploy a Svelte+SvelteKit application on Cloudflare Workers (free).
1) What to do in Cloudflare (dashboard)
- Create/use a Cloudflare account (Free plan).
- Go to
Develop -> Workers & Pagesand enable Workers on the account. - (Recommended) Create an API Token for deploy from CLI:
- Go to
Manage account -> Account API Tokens. - Under
Account Resources:select your account. Create token- Use the Edit Cloudflare Workers template (it has the minimum required permissions)
- If using a custom domain:
Zone -> DNS -> EditZone -> Worker Routes -> Edit(or use Custom Domain from UI)
- Select the
Zone Resourcesaccording to the zones the token will cover. - Click Continue to summary → Create Token.
-
If using a custom domain:
- Have the zone (
yourdomain.com) in Cloudflare. - On the deployed Worker:
Settings -> Domains & Routes -> Add Custom Domain
- Have the zone (
-
The new token can be tested:
curl "https://api.cloudflare.com/client/v4/accounts/xxx123/tokens/verify" \-H "Authorization: Bearer cfat_ABCD_my_token"2) What to configure in the project
The project must be aligned to a pure Worker:
- `main = "build/_worker.js"` - `[assets] directory = "build", binding = "ASSETS"`If you don’t have the account_id, add it:
account_id = "your_account_id"The account_id can be found in the Cloudflare dashboard, in the right sidebar of your domain, or with:
bunx wrangler whoamiIt is, of course, very important to use the adapter-cloudflare:
import adapter from '@sveltejs/adapter-cloudflare';Local requirements
- Dependencies:
- Node/npm, or Bun (recommended)
- Wrangler CLI
(via
npx wranglerorbunx wrangleralso works)
- Configure the token in your local environment
export CLOUDFLARE_API_TOKEN=cfat_ABCD_my_tokenOr permanently in ~/.bashrc, ~/.zshrc or the appropriate file for your shell:
echo 'export CLOUDFLARE_API_TOKEN=your_token_here' >> ~/.bashrcAlternatively with wrangler:
bunx wrangler loginThis opens a browser for OAuth. Personally, I don’t like it.
If you prefer a direct token:
bunx wrangler config3) Variables and secrets (important)
In your current app
If import.meta.env.VITE_* (build-time) is used, that means:
- Variables must exist before
npm run buildorbun run build(.env, CI vars, etc). - They are not dynamically read from the Worker’s
envat runtime.
Critical examples could be:
VITE_APIVITE_API_KEY- Other
VITE_*for tracking/auth/etc.
Runtime secrets (optional)
If you later want to add secrets at runtime:
bunx wrangler secret put VARIABLE_NAMEand read them from env.VARIABLE_NAME.
4) Build + deploy (step by step)
In the following steps you can replace npm with bun in the commands:
- Install dependencies:
npm ci- Configure token:
export CLOUDFLARE_API_TOKEN=your_token-
Prepare the build
.env(with your correctVITE_*values). -
Build:
make build# npm run build# bun run build- Verify output:
build/_worker.jsbuild/_app/...
- Test the Worker locally:
make preview# bunx wrangler dev- Deploy:
make deploy# bunx wrangler deploy- Test the Worker, which will be available at:
https://example-frontend.your-subdomain.workers.dev
or at your custom domain if configured in the dashboard.
5) OIDC configuration (Zitadel or another provider) for production
If using an OIDC provider such as Zitadel, you must register the required endpoints with the provider:
- Redirect URI(s) (login callback).
- Post-logout redirect URI(s).
- Web origins of the frontend.
6) Post-deploy checks
To view logs:
npx wrangler tail7) Free Workers limits to consider (reviewed on April 7, 2026)
- 100,000 requests/day.
- CPU 10ms per request.
- 50 external subrequests per request.
- 128 MB of memory.
- Worker size: 3 MB.
- Static assets per version: up to 20,000 files.
If you exceed these limits, you will get 1027/quota errors.
8) Typical errors
Cannot read properties of undefined (reading 'fetch')
- Common cause: missing
ASSETSbinding or deploy run in wrong mode (Pages vs Worker). - In your current state this is already fixed for Worker.
- Empty
VITE_*variables in production
- Built without the correct
.env.
- Zitadel login fails in prod
- Redirect URI / origins not registered for the final domain.
9) Deploy on custom domain
There are 2 ways to do this, assuming the domain is in Cloudflare:
A. Using wrangler.toml
Configure the routes:
routes = [ { pattern = "www.example.com/*", zone_name = "example.com" }]Then do the normal deploy:
bunx wrangler deployB. In the Cloudflare dashboard
- Go to Workers & Pages → your worker → Settings → Triggers
- Click Add Custom Domain
- Type
www.example.com - Cloudflare automatically creates the DNS record and TLS certificate
Domain outside Cloudflare
If the domain is not in Cloudflare:
You must first add the domain to Cloudflare (or at least use an external DNS record):
In your registrar’s DNS, create a CNAME record:
In tinydns, for example:
Cwww:your-project.your-subdomain.workers.dev:86400Then in Cloudflare, go to the worker and in Triggers click Add Custom Domain and add www.example.com.
Difference between routes and custom_domain
| Feature | routes | custom_domain |
|---|---|---|
| Requires zone in CF | Yes | Yes |
| Configurable in toml | Yes | Yes |
| Automatic TLS | Manual (already existing) | Automatic |
| Shared with other workers | Yes (by pattern) | No |
For a subdomain dedicated to the worker, custom_domain is the cleanest option:
routes = [ { pattern = "ssr.example.com/*", custom_domain = true, zone_name = "example.com" }]Or the alternative syntax:
[[env.production.routes]]pattern = "ssr.example.com/*"zone_name = "example.com"Bonus: Useful Makefile rules
deploy: bunx wrangler deploy
preview: bunx wrangler dev
build: @echo "Building with $(DOTENV_FILE)" DOTENV_FILE=$(DOTENV_FILE) bun run buildOfficial sources used
- Workers limits: https://developers.cloudflare.com/workers/platform/limits/
- Workers pricing: https://developers.cloudflare.com/workers/platform/pricing/
- Wrangler config (
assets): https://developers.cloudflare.com/workers/wrangler/configuration/ - Static assets binding: https://developers.cloudflare.com/workers/static-assets/binding/
- Environment vars/secrets:
- Routing/domains:
