diff --git a/.changeset/few-walls-obey.md b/.changeset/few-walls-obey.md
new file mode 100644
index 000000000000..b089af1ccb75
--- /dev/null
+++ b/.changeset/few-walls-obey.md
@@ -0,0 +1,12 @@
+---
+'@sveltejs/adapter-cloudflare-workers': patch
+---
+
+[Breaking] refactor implementation from "Service Worker" pattern to "Module Worker" used in adapter-cloudflare
+
+#### add the following to your wrangler.toml
+```toml
+		[build.upload]
+		format = "modules"
+		main = "./worker.mjs"
+```
diff --git a/packages/adapter-cloudflare-workers/README.md b/packages/adapter-cloudflare-workers/README.md
index eb6420d22882..59d26dc25296 100644
--- a/packages/adapter-cloudflare-workers/README.md
+++ b/packages/adapter-cloudflare-workers/README.md
@@ -53,7 +53,6 @@ Then configure your sites build directory and your account-details in the config
 ```toml
 account_id = 'YOUR ACCOUNT_ID'
 zone_id    = 'YOUR ZONE_ID' # optional, if you don't specify this a workers.dev subdomain will be used.
-site = {bucket = "./build", entry-point = "./workers-site"}
 
 type = "javascript"
 
@@ -62,7 +61,12 @@ type = "javascript"
 command = ""
 
 [build.upload]
-format = "service-worker"
+format = "modules"
+main = "./worker.mjs"
+
+[site]
+bucket = "./.cloudflare/assets"
+entry-point = "./.cloudflare/worker"
 ```
 
 It's recommended that you add the `build` and `workers-site` folders (or whichever other folders you specify) to your `.gitignore`.
diff --git a/packages/adapter-cloudflare-workers/ambient.d.ts b/packages/adapter-cloudflare-workers/ambient.d.ts
index 9df439aab626..a1381f25f04d 100644
--- a/packages/adapter-cloudflare-workers/ambient.d.ts
+++ b/packages/adapter-cloudflare-workers/ambient.d.ts
@@ -9,9 +9,7 @@ declare module 'MANIFEST' {
 	export const prerendered: Set<string>;
 }
 
-declare abstract class FetchEvent extends Event {
-	readonly request: Request;
-	respondWith(promise: Response | Promise<Response>): void;
-	passThroughOnException(): void;
-	waitUntil(promise: Promise<any>): void;
+declare module '__STATIC_CONTENT_MANIFEST' {
+	const json: string;
+	export default json;
 }
diff --git a/packages/adapter-cloudflare-workers/files/entry.js b/packages/adapter-cloudflare-workers/files/entry.js
index 9c778e8c109a..ab6d9b602d41 100644
--- a/packages/adapter-cloudflare-workers/files/entry.js
+++ b/packages/adapter-cloudflare-workers/files/entry.js
@@ -1,61 +1,101 @@
 import { Server } from 'SERVER';
 import { manifest, prerendered } from 'MANIFEST';
 import { getAssetFromKV } from '@cloudflare/kv-asset-handler';
+import static_asset_manifest_json from '__STATIC_CONTENT_MANIFEST';
+const static_asset_manifest = JSON.parse(static_asset_manifest_json);
 
 const server = new Server(manifest);
 
 const prefix = `/${manifest.appDir}/`;
 
-addEventListener('fetch', (/** @type {FetchEvent} */ event) => {
-	event.respondWith(handle(event));
-});
+export default {
+	/**
+	 * @param {Request} req
+	 * @param {any} env
+	 * @param {any} context
+	 */
+	async fetch(req, env, context) {
+		const url = new URL(req.url);
 
-/**
- * @param {FetchEvent} event
- * @returns {Promise<Response>}
- */
-async function handle(event) {
-	const { request } = event;
-
-	const url = new URL(request.url);
-
-	// generated assets
-	if (url.pathname.startsWith(prefix)) {
-		const res = await getAssetFromKV(event);
-		return new Response(res.body, {
-			headers: {
-				'cache-control': 'public, immutable, max-age=31536000',
-				'content-type': res.headers.get('content-type')
+		// static assets
+		if (url.pathname.startsWith(prefix)) {
+			/** @type {Response} */
+			const res = await get_asset_from_kv(req, env, context);
+			if (is_error(res.status)) {
+				return res;
 			}
-		});
-	}
+			return new Response(res.body, {
+				headers: {
+					// include original cache headers, minus cache-control which
+					// is overridden, and etag which is no longer useful
+					'cache-control': 'public, immutable, max-age=31536000',
+					'content-type': res.headers.get('content-type'),
+					'x-robots-tag': 'noindex'
+				}
+			});
+		}
 
-	// prerendered pages and index.html files
-	const pathname = url.pathname.replace(/\/$/, '');
-	let file = pathname.substring(1);
+		// prerendered pages and index.html files
+		const pathname = url.pathname.replace(/\/$/, '');
+		let file = pathname.substring(1);
 
-	try {
-		file = decodeURIComponent(file);
-	} catch (err) {
-		// ignore
-	}
+		try {
+			file = decodeURIComponent(file);
+		} catch (err) {
+			// ignore
+		}
+
+		if (
+			manifest.assets.has(file) ||
+			manifest.assets.has(file + '/index.html') ||
+			prerendered.has(pathname || '/')
+		) {
+			return get_asset_from_kv(req, env, context);
+		}
 
-	if (
-		manifest.assets.has(file) ||
-		manifest.assets.has(file + '/index.html') ||
-		prerendered.has(pathname || '/')
-	) {
-		return await getAssetFromKV(event);
+		// dynamically-generated pages
+		try {
+			return await server.respond(req, {
+				platform: { env, context },
+				getClientAddress() {
+					return req.headers.get('cf-connecting-ip');
+				}
+			});
+		} catch (e) {
+			return new Response('Error rendering route: ' + (e.message || e.toString()), { status: 500 });
+		}
 	}
+};
 
-	// dynamically-generated pages
+/**
+ * @param {Request} req
+ * @param {any} env
+ * @param {any} context
+ */
+async function get_asset_from_kv(req, env, context) {
 	try {
-		return await server.respond(request, {
-			getClientAddress() {
-				return request.headers.get('cf-connecting-ip');
+		return await getAssetFromKV(
+			{
+				request: req,
+				waitUntil(promise) {
+					return context.waitUntil(promise);
+				}
+			},
+			{
+				ASSET_NAMESPACE: env.__STATIC_CONTENT,
+				ASSET_MANIFEST: static_asset_manifest
 			}
-		});
+		);
 	} catch (e) {
-		return new Response('Error rendering route:' + (e.message || e.toString()), { status: 500 });
+		const status = is_error(e.status) ? e.status : 500;
+		return new Response(e.message || e.toString(), { status });
 	}
 }
+
+/**
+ * @param {number} status
+ * @returns {boolean}
+ */
+function is_error(status) {
+	return status > 399;
+}
diff --git a/packages/adapter-cloudflare-workers/index.js b/packages/adapter-cloudflare-workers/index.js
index f161a109cb37..94732b553f06 100644
--- a/packages/adapter-cloudflare-workers/index.js
+++ b/packages/adapter-cloudflare-workers/index.js
@@ -6,12 +6,12 @@ import toml from '@iarna/toml';
 import { fileURLToPath } from 'url';
 
 /** @type {import('.')} */
-export default function () {
+export default function (options = {}) {
 	return {
 		name: '@sveltejs/adapter-cloudflare-workers',
 
 		async adapt(builder) {
-			const { site } = validate_config(builder);
+			const { site, build } = validate_config(builder);
 
 			// @ts-ignore
 			const { bucket } = site;
@@ -19,6 +19,9 @@ export default function () {
 			// @ts-ignore
 			const entrypoint = site['entry-point'] || 'workers-site';
 
+			// @ts-ignore
+			const main_path = build.upload.main;
+
 			const files = fileURLToPath(new URL('./files', import.meta.url).href);
 			const tmp = builder.getBuildDirectory('cloudflare-workers-tmp');
 
@@ -50,14 +53,20 @@ export default function () {
 			);
 
 			await esbuild.build({
+				target: 'es2020',
+				platform: 'browser',
+				...options,
 				entryPoints: [`${tmp}/entry.js`],
-				outfile: `${entrypoint}/index.js`,
+				outfile: `${entrypoint}/${main_path}`,
 				bundle: true,
-				target: 'es2020',
-				platform: 'browser'
+				external: ['__STATIC_CONTENT_MANIFEST', ...(options?.external || [])],
+				format: 'esm'
 			});
 
-			writeFileSync(`${entrypoint}/package.json`, JSON.stringify({ main: 'index.js' }));
+			writeFileSync(
+				`${entrypoint}/package.json`,
+				JSON.stringify({ main: main_path, type: 'module' })
+			);
 
 			builder.log.minor('Copying assets...');
 			builder.writeClient(bucket);
@@ -86,6 +95,36 @@ function validate_config(builder) {
 			);
 		}
 
+		// @ts-ignore
+		if (!wrangler_config.build || !wrangler_config.build.upload) {
+			throw new Error(
+				'You must specify build.upload options in wrangler.toml. Consult https://github.com/sveltejs/kit/tree/master/packages/adapter-cloudflare-workers'
+			);
+		}
+
+		// @ts-ignore
+		if (wrangler_config.build.upload.format !== 'modules') {
+			throw new Error('build.upload.format in wrangler.toml must be "modules"');
+		}
+
+		// @ts-ignore
+		const main_file = wrangler_config.build?.upload?.main;
+		const main_file_ext = main_file?.split('.').slice(-1)[0];
+		if (main_file_ext && main_file_ext !== 'mjs') {
+			// @ts-ignore
+			const upload_rules = wrangler_config.build?.upload?.rules;
+			// @ts-ignore
+			const matching_rule = upload_rules?.find(({ globs }) =>
+				// @ts-ignore
+				globs.find((glob) => glob.endsWith(`*.${main_file_ext}`))
+			);
+			if (!matching_rule) {
+				throw new Error(
+					'To support a build.upload.main value not ending in .mjs, an upload rule must be added to build.upload.rules. Consult https://developers.cloudflare.com/workers/cli-wrangler/configuration/#build'
+				);
+			}
+		}
+
 		return wrangler_config;
 	}
 
@@ -104,6 +143,13 @@ function validate_config(builder) {
 		route = ""
 		zone_id = ""
 
+		[build]
+    command = ""
+
+		[build.upload]
+		format = "modules"
+		main = "./worker.mjs"
+
 		[site]
 		bucket = "./.cloudflare/assets"
 		entry-point = "./.cloudflare/worker"`