diff --git a/.changeset/olive-lemons-dance.md b/.changeset/olive-lemons-dance.md
new file mode 100644
index 00000000..4318d8a8
--- /dev/null
+++ b/.changeset/olive-lemons-dance.md
@@ -0,0 +1,5 @@
+---
+"@opennextjs/cloudflare": patch
+---
+
+Fix multiple Windows path issues.
diff --git a/.github/actions/setup-playwright/action.yml b/.github/actions/setup-playwright/action.yml
new file mode 100644
index 00000000..161da717
--- /dev/null
+++ b/.github/actions/setup-playwright/action.yml
@@ -0,0 +1,27 @@
+name: "Setup Playwright"
+description: "Setup Playwright with caching"
+
+runs:
+  using: "composite"
+  steps:
+    - name: Put $HOME in env
+      if: runner.os == 'windows'
+      shell: pwsh
+      run: echo "HOME=$HOME" | Out-File -FilePath $env:GITHUB_ENV -Append
+
+    - name: Cache Playwright
+      id: playwright-cache
+      uses: actions/cache@v4
+      with:
+        path: ${{ runner.os == 'Windows' && format('{0}{1}', env.HOME, '\AppData\Local\ms-playwright') || runner.os == 'Linux' && '~/.cache/ms-playwright' || '~/Library/Caches/ms-playwright' }}
+        key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
+
+    - name: Install Playwright with dependencies
+      if: steps.playwright-cache.outputs.cache-hit != 'true'
+      shell: bash
+      run: pnpm playwright install --with-deps
+
+    - name: Install Playwright's dependencies
+      if: steps.playwright-cache.outputs.cache-hit == 'true'
+      shell: bash
+      run: pnpm playwright install-deps
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index 487f826e..b64c8f1f 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -17,7 +17,7 @@ jobs:
         uses: ./.github/actions/install-dependencies
 
       - name: Install Playwright
-        run: pnpm run install-playwright
+        uses: ./.github/actions/setup-playwright
 
       - name: Build the tool
         run: pnpm build
diff --git a/packages/cloudflare/src/cli/build/patches/plugins/instrumentation.ts b/packages/cloudflare/src/cli/build/patches/plugins/instrumentation.ts
index c8335a1a..37f03865 100644
--- a/packages/cloudflare/src/cli/build/patches/plugins/instrumentation.ts
+++ b/packages/cloudflare/src/cli/build/patches/plugins/instrumentation.ts
@@ -5,6 +5,8 @@ import { type BuildOptions, getPackagePath } from "@opennextjs/aws/build/helper.
 import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
 import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js";
 
+import { normalizePath } from "../../utils/normalize-path.js";
+
 export function patchInstrumentation(updater: ContentUpdater, buildOpts: BuildOptions): Plugin {
   const builtInstrumentationPath = getBuiltInstrumentationPath(buildOpts);
 
@@ -84,7 +86,7 @@ function getBuiltInstrumentationPath(buildOpts: BuildOptions): string | null {
     getPackagePath(buildOpts),
     `.next/server/${INSTRUMENTATION_HOOK_FILENAME}.js`
   );
-  return existsSync(maybeBuiltInstrumentationPath) ? maybeBuiltInstrumentationPath : null;
+  return existsSync(maybeBuiltInstrumentationPath) ? normalizePath(maybeBuiltInstrumentationPath) : null;
 }
 
 /**
diff --git a/packages/cloudflare/src/cli/build/patches/plugins/wrangler-external.ts b/packages/cloudflare/src/cli/build/patches/plugins/wrangler-external.ts
index a0c975bb..df66864d 100644
--- a/packages/cloudflare/src/cli/build/patches/plugins/wrangler-external.ts
+++ b/packages/cloudflare/src/cli/build/patches/plugins/wrangler-external.ts
@@ -18,6 +18,8 @@ import { dirname, resolve } from "node:path";
 
 import type { PluginBuild } from "esbuild";
 
+import { normalizePath } from "../../utils/normalize-path.js";
+
 export function setWranglerExternal() {
   return {
     name: "wrangler-externals",
@@ -28,7 +30,7 @@ export function setWranglerExternal() {
       //TODO: Ideally in the future we would like to analyze the files in case they are using wasm in a Node way (i.e. WebAssembly.instantiate)
       build.onResolve({ filter: /(\.bin|\.wasm(\?module)?)$/ }, ({ path, importer }) => {
         return {
-          path: resolve(dirname(importer), path),
+          path: normalizePath(resolve(dirname(importer), path)),
           namespace,
           external: true,
         };
diff --git a/packages/cloudflare/src/cli/commands/populate-cache.ts b/packages/cloudflare/src/cli/commands/populate-cache.ts
index c05a5029..101320b6 100644
--- a/packages/cloudflare/src/cli/commands/populate-cache.ts
+++ b/packages/cloudflare/src/cli/commands/populate-cache.ts
@@ -33,6 +33,7 @@ import {
   BINDING_NAME as D1_TAG_BINDING_NAME,
   NAME as D1_TAG_NAME,
 } from "../../api/overrides/tag-cache/d1-next-tag-cache.js";
+import { normalizePath } from "../build/utils/normalize-path.js";
 import type { WranglerTarget } from "../utils/run-wrangler.js";
 import { runWrangler } from "../utils/run-wrangler.js";
 
@@ -57,8 +58,8 @@ export function getCacheAssets(opts: BuildOptions): CacheAsset[] {
   const assets: CacheAsset[] = [];
 
   for (const file of allFiles) {
-    const fullPath = file.fullpathPosix();
-    const relativePath = path.relative(path.join(opts.outputDir, "cache"), fullPath);
+    const fullPath = file.fullpath();
+    const relativePath = normalizePath(path.relative(path.join(opts.outputDir, "cache"), fullPath));
 
     if (relativePath.startsWith("__fetch")) {
       const [__fetch, buildId, ...keyParts] = relativePath.split("/");
@@ -129,7 +130,11 @@ async function populateR2IncrementalCache(
     });
     runWrangler(
       options,
-      ["r2 object put", quoteShellMeta(path.join(bucket, cacheKey)), `--file ${quoteShellMeta(fullPath)}`],
+      [
+        "r2 object put",
+        quoteShellMeta(normalizePath(path.join(bucket, cacheKey))),
+        `--file ${quoteShellMeta(fullPath)}`,
+      ],
       // NOTE: R2 does not support the environment flag and results in the following error:
       // Incorrect type for the 'cacheExpiry' field on 'HttpMetadata': the provided value is not of type 'date'.
       { target: populateCacheOptions.target, logging: "error" }