# How to Debug Next.js Applications Running on Cloudflare Workers: Local and Production Techniques

> Debug Next.js on Cloudflare Workers with `wrangler dev` for local debugging and `wrangler tail` for production log streaming. Master edge application troubleshooting.

- Repository: [Muhammad Arifin/fullstack-next-cloudflare](https://github.com/ifindev/fullstack-next-cloudflare)
- Tags: how-to-guide
- Published: 2026-03-04

---

**Debug Next.js applications running on Cloudflare Workers by using `wrangler dev` for local simulation with source maps and `console.log` statements, or `wrangler tail` for streaming production logs from the edge.**

The `ifindev/fullstack-next-cloudflare` repository demonstrates a modern full-stack architecture combining **Next.js 15** (App Router) with **Cloudflare Workers** via the `@opennextjs/cloudflare` integration. Because server-side code executes inside the Worker runtime rather than a local Node.js process, traditional debugging methods like `next dev` inspection are insufficient for API routes, Server Actions, and database interactions.

## Understanding the Debug Architecture

When you run the build process, `@opennextjs/cloudflare` generates a Worker bundle at [`.open-next/worker.js`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/.open-next/worker.js) and defines asset bindings in **`wrangler.jsonc`**. Next.js server-side code—including API routes, Server Actions, and middleware—runs inside the Worker runtime, while React components render on the edge.

The repository provides three distinct development entry points for debugging:

- **`npm run dev`** (`next dev`): Starts a standard Next.js dev server locally. Use this only for pure frontend UI work that does not interact with Worker-bound APIs.
- **`npm run dev:cf`** (`npx @opennextjs/cloudflare build && wrangler dev`): Builds the Cloudflare-compatible bundle and launches **Wrangler dev**, which runs the Worker locally with full access to D1, R2, and AI bindings.
- **`npm run dev:remote`** (`npx @opennextjs/cloudflare build && wrangler dev --remote`): Deploys to a temporary remote preview and streams logs. Use this for debugging environment-specific behavior or production-only bindings.

## Local Debugging with Wrangler Dev

Local debugging requires the `dev:cf` script, which respects the bindings defined in **`wrangler.jsonc`** (D1 databases, R2 buckets, AI, etc.) and generates source maps that map stack traces back to original TypeScript files.

### Using console.log and Source Maps

The Worker runtime forwards `console.log`, `console.error`, and `console.debug` calls to your terminal. When `@opennextjs/cloudflare` builds the project, it generates `.open-next/worker.js.map`, enabling Wrangler to display original file paths in stack traces.

In [`src/modules/auth/auth.route.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/src/modules/auth/auth.route.ts), add logging to trace request flow:

```typescript
// src/modules/auth/auth.route.ts
export const authRoutes = createRoute({
  path: "/login",
  method: "GET",
  handler: async (c) => {
    console.log("Login request received", c.req.url);
    // Stack traces will point to this exact line number
  },
});

```

When `wrangler dev` executes this route, the terminal displays the original TypeScript location:

```

[2024-03-04 12:34:56] LOG  src/modules/auth/auth.route.ts:8:13 Login request received

```

### Setting Breakpoints with debugger Statements

Insert `debugger;` statements in server-side files to pause execution. Attach the Node inspector by running Wrangler with the inspect flag:

```bash
node --inspect-brk $(which wrangler) dev

```

Then open Chrome at `chrome://inspect` and attach to the Worker process. Breakpoints will resolve to the original TypeScript source thanks to the source map.

Example breakpoint in R2 upload logic:

```typescript
// src/lib/r2.ts
export async function uploadFile(key: string, body: Uint8Array) {
  console.log("Uploading to R2:", key);
  debugger; // Execution pauses here when inspector is attached
  await env.next_cf_app_bucket.put(key, body);
}

```

## Debugging Production Deployments

For issues that only appear in the Cloudflare edge environment, use remote debugging and log streaming capabilities.

### Streaming Live Logs with wrangler tail

The `wrangler tail` command streams real-time logs from deployed Workers. Use JSON formatting for structured analysis:

```bash

# Stream all logs with full detail

wrangler tail --format=json --level=debug

# Filter for errors only

wrangler tail --level=error

```

JSON output includes source file references via source maps:

```json
{
  "timestamp": "2024-03-04T12:35:00.123Z",
  "level": "error",
  "msg": "src/modules/auth/auth.route.ts:12:9 Unhandled exception: ...",
  "source": "worker"
}

```

Run `npm run dev:remote` to deploy a temporary preview and attach Chrome DevTools via the WebSocket URL printed by Wrangler (e.g., `ws://127.0.0.1:9229/...`). This allows setting breakpoints in original TS files while code executes on Cloudflare's edge.

## Common Debugging Scenarios

Apply these patterns to diagnose specific issues in the Next.js Cloudflare stack.

### Debugging Server Actions

Server Actions failing silently require explicit error handling and logging. Wrap action bodies in `try/catch` blocks and run `dev:cf` to capture stack traces:

```typescript
// src/modules/todos/actions/create-todo.action.ts
export async function createTodo(data: TodoCreate) {
  try {
    console.log("Creating todo:", data);
    // D1 insertion logic...
  } catch (e) {
    console.error("Server Action failed:", e);
    throw e; // Re-throw to maintain error boundaries
  }
}

```

The source map ensures the stack trace references the original [`create-todo.action.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/create-todo.action.ts) file, not the compiled Worker bundle.

### Inspecting D1 Database Queries

Log query results in [`src/db/index.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/src/db/index.ts) or specific action files to verify database interactions:

```typescript
// src/modules/todos/actions/get-todos.action.ts
export async function getAllTodos(): Promise<Todo[]> {
  const result = await env.next_cf_app.prepare("SELECT * FROM todos").all<Todo>();
  console.log("D1 rows returned:", result.results?.length);
  console.timeEnd('fetch-todos'); // Performance timing
  return result.results ?? [];
}

```

Use `console.time()` and `console.timeEnd()` to profile expensive queries during local development.

### Troubleshooting R2 Uploads

Verify bucket bindings and upload responses in [`src/lib/r2.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/src/lib/r2.ts):

```typescript
// src/lib/r2.ts
export async function uploadFile(key: string, body: Uint8Array) {
  console.log("R2 bucket:", env.next_cf_app_bucket.name);
  const response = await env.next_cf_app_bucket.put(key, body);
  console.log("R2 upload status:", response.httpStatus);
}

```

Worker logs appear in the terminal during `wrangler dev`; use `wrangler tail` for remote debugging in production.

### Enabling Verbose AI Logging

For the Cloudflare Workers AI binding (`AI`), add verbose logging via environment variables in **`wrangler.jsonc`**:

```json
{
  "vars": {
    "DEBUG": "*"
  }
}

```

This surfaces AI inference errors and debug information in the Wrangler console, as implemented in [`src/services/summarizer.service.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/src/services/summarizer.service.ts).

## Configuration for Enhanced Debugging

Modify **`wrangler.jsonc`** to optimize the debugging experience. The `ASSETS` binding serves static files from `.open-next`, while `vars` injects environment variables into the Worker context:

```json
{
  "name": "next-cf-app",
  "main": ".open-next/worker.js",
  "compatibility_date": "2024-09-23",
  "assets": {
    "directory": ".open-next/assets",
    "binding": "ASSETS"
  },
  "vars": {
    "DEBUG": "*",
    "NODE_ENV": "development"
  }
}

```

Ensure [`next.config.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/next.config.ts) loads Cloudflare dev helpers when `NODE_ENV=development` to maintain compatibility with the local Worker runtime.

## Summary

- **Use `npm run dev:cf`** for full-stack debugging with local Worker simulation, D1, R2, and AI bindings.
- **Leverage source maps** generated by `@opennextjs/cloudflare` in `.open-next/worker.js.map` to see original TypeScript file paths in stack traces.
- **Insert `debugger;` statements** and attach the Node inspector with `node --inspect-brk` for breakpoint debugging.
- **Stream production logs** via `wrangler tail --format=json` to diagnose edge-specific issues after deployment.
- **Configure `wrangler.jsonc` vars** like `DEBUG: "*"` to enable verbose logging for AI and other bindings.

## Frequently Asked Questions

### Why doesn't `console.log` appear when I run `next dev`?

The standard `next dev` command runs a local Node.js server, not the Cloudflare Worker runtime. Server Actions, API routes, and middleware execute in the Node environment, so bindings like D1 and R2 are unavailable, and logs from Worker-specific code won't appear. Use **`npm run dev:cf`** instead to run `wrangler dev`, which executes your Next.js server code inside the actual Worker runtime and streams `console.log` output to your terminal.

### How do I debug Server Actions that fail silently on Cloudflare?

Wrap the Server Action body in a `try/catch` block and explicitly log the error before re-throwing. Run the action via **`npm run dev:cf`** and watch the Wrangler console. Because `@opennextjs/cloudflare` generates source maps, the stack trace will point to the original TypeScript file (e.g., `src/modules/todos/actions/create-todo.action.ts:15`) rather than the minified Worker bundle, allowing you to identify the exact line causing the failure.

### Can I use Chrome DevTools to debug a deployed Worker?

Yes. Run **`npm run dev:remote`** to deploy a temporary preview, then use the WebSocket URL printed by Wrangler (typically `ws://127.0.0.1:9229/...`) to attach Chrome DevTools. You can set breakpoints in your original TypeScript source files while the code executes on Cloudflare's edge infrastructure, enabling debugging of production-specific behavior like real D1 data or R2 buckets.

### How do I view logs from a production deployment?

Use the **`wrangler tail`** command to stream real-time logs from your live Worker. Add the `--format=json` flag for structured output and `--level=debug` for verbose detail. The source maps generated during the build process ensure that production error logs reference original file paths like [`src/modules/auth/auth.route.ts`](https://github.com/ifindev/fullstack-next-cloudflare/blob/main/src/modules/auth/auth.route.ts), making it possible to correlate edge errors with your local source code.