Unable to deploy Fresh to Cloudflare Workers
Hi, I'm trying to deploy a Fresh project to Cloudflare Workers, but I've been unsuccessful.
Here are the steps I followed:
-
I created a new project using the following command:
deno run -Ar jsr:@fresh/init -
I followed the Cloudflare Workers | Fresh docs and added the required code to
vite.config.tsfile. -
I added a
wrangler.jsoncfile with the following content:{ "$schema": "node_modules/wrangler/config-schema.json", "name": "fresh2-example", "main": "./main.ts", "compatibility_date": "2025-04-01", "compatibility_flags": ["nodejs_compat"], "observability": { "enabled": true }, "upload_source_maps": true } -
I added the following code to the end of my
main.tsfile:export default { fetch: app.handler(), }; -
I pushed the project to a GitHub repository, which you can find here: mzl980425/fresh2-example. Then, I created a new Worker on Cloudflare and connected it to this repository. The worker's build configuration is as follows:
- Build command:
npx deno task build - Deploy command:
npx wrangler deploy
- Build command:
-
After the deployment was complete, I found that static assets are served correctly (e.g.,
https://xxx.workers.dev/favicon.ico,https://xxx.workers.dev/logo.svg). However, all other page and API routes return a "NOT FOUND" error, including the root page (https://xxx.workers.dev/) and API routes like (https://xxx.workers.dev/api2/world).
Is there something wrong with my code or configuration? Any help would be greatly appreciated.
Thanks
Thanks for sharing the detailed steps of what you tried. It looks like step 4 is wrong. The proper entry point is autogenerated by fresh and is available under _fresh/server.js after having run deno task build. Directly importing from main.ts will not work because it's missing the autogenerated code that sets up the route manifest and much more.
I reverted step 4, and updated my wrangler.jsonc file, changing the main entry to "./_fresh/server.js".
Then I run deno task build, the following error is in the console:
I can confirm that just putting in wrangler.jsonc:
"main": "./_fresh/server.js"
Will produce this error:
Task deploy deno task build && wrangler deploy
Task build vite build
vite v7.1.5 building for production...
✓ 25 modules transformed.
_fresh/client/.vite/manifest.json 0.84 kB │ gzip: 0.28 kB
_fresh/client/assets/client-entry-BD_nbrQW.css 7.08 kB │ gzip: 2.31 kB
_fresh/client/assets/Counter-CcJtgLXz.js 0.82 kB │ gzip: 0.51 kB
_fresh/client/assets/client-snapshot-DkFxL7r5.js 1.33 kB │ gzip: 0.78 kB
_fresh/client/assets/preact.module-BC68DR7w.js 10.88 kB │ gzip: 4.62 kB
_fresh/client/assets/client-entry-Dj0PfJKP.js 24.60 kB │ gzip: 9.11 kB
✓ built in 627ms
vite v7.1.5 building SSR bundle for production...
vite v7.1.5 building SSR bundle for production... (x2)
✓ 1 modules transformed.
✗ Build failed in 1.15s
error during build:
[vite:define] Transform failed with 1 error:
/cloudflare-test/_fresh/server/server-entry.mjs:220:11943: ERROR: Multiple exports with the same name "default"
file: /cloudflare-test/_fresh/server/server-entry.mjs
at failureErrorWithLog (/cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:1467:15)
at /cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:736:50
at responseCallbacks.<computed> (/cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:603:9)
at handleIncomingPacket (/cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:658:12)
at Readable.readFromStdout (/cloudflare-test/node_modules/.deno/[email protected]/node_modules/esbuild/lib/main.js:581:7)
at Readable.emit (ext:deno_node/_events.mjs:436:20)
at addChunk (node:_stream_readable:452:12)
at readableAddChunkPushByteMode (node:_stream_readable:413:3)
at Readable.push (node:_stream_readable:308:48)
at ext:deno_node/internal/webstreams/adapters.js:74:22
at eventLoopTick (ext:core/01_core.js:179:7)
Bare bones reproduction repo -> https://github.com/fry69/fresh-vite-cloudflare-repro
(I even tested it with _fresh/server.js instead of ./_fresh/server.js, same error)
I'm not able to reproduce that Multiple exports with the same name "default" error. The build passes just fine. Had to point it to a different entrypoint though because it seems like the cloudflare vite plugin throws an error now when a file doesn't exist when you start the build, see https://github.com/denoland/fresh/pull/3405
Looking at the _fresh/server-entry.mjs file everything is correct too. It sounds very much like an issue in wrangler.
If I try the approach with the server.js file, I get this error:
Task build vite build
vite v7.1.5 building for production...
✓ 25 modules transformed.
_fresh/client/.vite/manifest.json 0.84 kB │ gzip: 0.28 kB
_fresh/client/assets/client-entry-D1ibAoFr.css 6.92 kB │ gzip: 2.27 kB
_fresh/client/assets/Counter-CcJtgLXz.js 0.82 kB │ gzip: 0.51 kB
_fresh/client/assets/client-snapshot-DkFxL7r5.js 1.33 kB │ gzip: 0.78 kB
_fresh/client/assets/preact.module-BC68DR7w.js 10.88 kB │ gzip: 4.62 kB
_fresh/client/assets/client-entry-D8t_PZsc.js 24.60 kB │ gzip: 9.11 kB
✓ built in 627ms
vite v7.1.5 building SSR bundle for production...
vite v7.1.5 building SSR bundle for production... (x2)
✓ 1 modules transformed.
✗ Build failed in 681ms
error during build:
[deno] Could not load /cloudflare-test/_fresh/server.js (imported by server.js): Import 'file:///cloudflare-test/_fresh/server.js' failed, not found.
at __wbindgen_error_new (https://jsr.io/@deno/loader/0.3.5/src/lib/rs_lib.internal.js:1073:15)
at wasm://wasm/01438d62:wasm-function[11086]:0x404151
at wasm://wasm/01438d62:wasm-function[4736]:0x39979c
at wasm://wasm/01438d62:wasm-function[118]:0x3e24d
at wasm://wasm/01438d62:wasm-function[1983]:0x3011cb
at wasm://wasm/01438d62:wasm-function[10418]:0x3fb858
at wasm://wasm/01438d62:wasm-function[11766]:0x4081aa
at __wbg_adapter_54 (https://jsr.io/@deno/loader/0.3.5/src/lib/rs_lib.internal.js:252:8)
at real (https://jsr.io/@deno/loader/0.3.5/src/lib/rs_lib.internal.js:156:14)
at ext:core/01_core.js:295:9
at eventLoopTick (ext:core/01_core.js:179:7)
$ wrangler --version
⛅️ wrangler 4.35.0
───────────────────
I assume the trick with the server.js file only works if a prior build already has generated artifacts in _fresh, but not with a clean slate.
Gosh that sounds bad. I successfully deployed Fresh to CF workers when working on the vite plugin a while ago, but it seems like they did a bunch of changes that broke it.
Curiously enough, having the _fresh/server.js already there seems to fix the build issue. Maybe the @fresh/plugin-vite should create that file earlier.
As a temporary fix, I found out that adding a _fresh/server.js before starting the build with the contents: "export default {}" seems to fix it. I suggest even comitting it to the repo so it stays there. I will try to look into it more.
@marvinhagemeister Is this issue currently assigned to someone? It seems the Cloudflare solution is not compatible with clean pipeline builds
Curiously enough, having the
_fresh/server.jsalready there seems to fix the build issue. Maybe the@fresh/plugin-viteshould create that file earlier.As a temporary fix, I found out that adding a
_fresh/server.jsbefore starting the build with the contents: "export default {}" seems to fix it. I suggest even comitting it to the repo so it stays there. I will try to look into it more.
This issue is still a problem for me. Thanks to this suggestion I was able to work around it by creating a temporary server.js. However this was not enough to get build and deploy pipelines working correctly for my project. I found that deno task build twice in a row allowed the "real" server.js to get picked up on the second time.
Of course once you figure out what the content of the server.js should be, you can just echo the correct content into the file and forget the second build, but that could be a pain to maintain and builds are fast.
This was the only way I could get GitHub Actions to work. Hopefully this is of some use for people trying to use workers in an automated way until a fix is found.