# Cloudflare

> Deploy Nitro apps to Cloudflare.

## Cloudflare Workers

**Preset:** `cloudflare_module`

:read-more{title="Cloudflare Workers" to="https://developers.cloudflare.com/workers/"}

::note
Integration with this provider is possible with [zero configuration](/deploy#zero-config-providers) supporting [workers builds (beta)](https://developers.cloudflare.com/workers/ci-cd/builds/).
::

The following shows an example `nitro.config.ts` file for deploying a Nitro app to Cloudflare Workers.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
    preset: "cloudflare_module"
})
```

### Local Preview

You can use [Wrangler](https://github.com/cloudflare/workers-sdk/tree/main/packages/wrangler) to preview your app locally:

:pm-run{script="build"}

:pm-x{command="wrangler dev"}

### Manual Deploy

After building your application you can manually deploy it with Wrangler.

First make sure to be logged into your Cloudflare account:

:pm-x{command="wrangler login"}

Then you can deploy the application with:

:pm-x{command="wrangler deploy"}

### Runtime Hooks

You can use the [runtime hooks](/docs/plugins#nitro-runtime-hooks) below to extend [Worker handlers](https://developers.cloudflare.com/workers/runtime-apis/handlers/).

:read-more{to="/docs/plugins#nitro-runtime-hooks"}

- [`cloudflare:scheduled`](https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/)
- [`cloudflare:email`](https://developers.cloudflare.com/email-routing/email-workers/runtime-api/)
- [`cloudflare:queue`](https://developers.cloudflare.com/queues/configuration/javascript-apis/#consumer)
- [`cloudflare:tail`](https://developers.cloudflare.com/workers/runtime-apis/handlers/tail/)
- `cloudflare:trace`
- `cloudflare:durable:init` (only with the [`cloudflare_durable`](#cloudflare-workers-with-durable-objects) preset)
- [`cloudflare:durable:alarm`](https://developers.cloudflare.com/durable-objects/api/alarms/) (only with the [`cloudflare_durable`](#cloudflare-workers-with-durable-objects) preset)

::note
The `cloudflare:queue` hook receives the message batch as `batch` and the `cloudflare:email` hook receives the incoming message as `message`. The older `event` field is deprecated for both hooks.
::

### Additional Exports

You can add an `exports.cloudflare.ts` file to your project root to export additional handlers or properties from the Cloudflare Worker entrypoint.

```ts [exports.cloudflare.ts]
export class MyWorkflow extends WorkflowEntrypoint {
  async run(event: WorkflowEvent, step: WorkflowStep) {
    // ...
  }
}
```

Nitro will automatically detect this file and include its exports in the final build.

::warning
The `exports.cloudflare.ts` file must not have a default export.
::

You can also customize the entrypoint file location using the `cloudflare.exports` option in your `nitro.config.ts`:

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  cloudflare: {
    exports: "custom-exports-entry.ts"
  }
})
```

### Scheduled Tasks (Cron Triggers)

When using [Nitro tasks](/docs/tasks) with `scheduledTasks`, Nitro automatically generates [Cron Triggers](https://developers.cloudflare.com/workers/configuration/cron-triggers/) in the wrangler config at build time.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  preset: "cloudflare_module",
  experimental: {
    tasks: true,
  },
  scheduledTasks: {
    "* * * * *": ["cms:update"],
    "0 15 1 * *": ["db:cleanup"],
  }
})
```

No manual Wrangler configuration is needed. Nitro handles it for you.

## Cloudflare Workers with Durable Objects

**Preset:** `cloudflare_durable`

:read-more{title="Durable Objects" to="https://developers.cloudflare.com/durable-objects/"}

This preset extends `cloudflare_module` and routes requests through a [Durable Object](https://developers.cloudflare.com/durable-objects/) instance, enabling stateful features such as WebSocket support (via [CrossWS](https://crossws.h3.dev/adapters/cloudflare#durable-objects)) and in-memory state that persists across requests.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  preset: "cloudflare_durable"
})
```

The preset entry exports a `$DurableObject` class. You need to declare the Durable Object binding and migration in your wrangler config:

```json [wrangler.json]
{
  "durable_objects": {
    "bindings": [
      {
        "name": "$DurableObject",
        "class_name": "$DurableObject"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_classes": ["$DurableObject"]
    }
  ]
}
```

You can use the `cloudflare:durable:init` runtime hook to run code when the Durable Object is initialized, and the `cloudflare:durable:alarm` hook to handle [alarms](https://developers.cloudflare.com/durable-objects/api/alarms/).

### Tracing

**🧪 Experimental!**

When the experimental [`tracingChannel`](/config#tracingchannel) option is enabled, the Cloudflare presets report Nitro's tracing-channel events (h3 routes and middleware, srvx, unstorage operations, …) as [custom spans](https://developers.cloudflare.com/workers/observability/traces/custom-spans/), alongside Cloudflare's automatic instrumentation (fetch calls, KV reads, D1 queries, …), with no OpenTelemetry SDK required.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  preset: "cloudflare_module",
  tracingChannel: true,
});
```

Tracing must be enabled on the Worker for spans to be recorded:

```jsonc [wrangler.jsonc]
{
  "observability": {
    "traces": {
      "enabled": true
    }
  }
}
```

## Cloudflare Pages

**Preset:** `cloudflare_pages`

:read-more{title="Cloudflare Pages" to="https://pages.cloudflare.com/"}

::note
Integration with this provider is possible with [zero configuration](/deploy#zero-config-providers).
::

::warning
Cloudflare [Workers](#cloudflare-workers) is the new recommended preset for deployments. Consider Cloudflare Pages only if you need Pages-specific features.
::

The following shows an example `nitro.config.ts` file for deploying a Nitro app to Cloudflare Pages.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
    preset: "cloudflare_pages"
})
```

Nitro automatically generates a `_routes.json` file that controls which routes get served from files and which are served from the Worker script. The auto-generated routes file can be overridden with the config option `cloudflare.pages.routes` ([read more](https://developers.cloudflare.com/pages/platform/functions/routing/#functions-invocation-routes)).

### Local Preview

You can use [Wrangler](https://github.com/cloudflare/workers-sdk/tree/main/packages/wrangler) to preview your app locally:

:pm-run{script="build"}

:pm-x{command="wrangler pages dev"}

### Manual Deploy

After building your application you can manually deploy it with Wrangler.

First make sure to be logged into your Cloudflare account:

:pm-x{command="wrangler login"}

Then you can deploy the application with:

:pm-x{command="wrangler pages deploy"}


## Deploy within CI/CD using GitHub Actions

Regardless of whether you're using Cloudflare Pages or Cloudflare Workers, you can use the [Wrangler GitHub actions](https://github.com/marketplace/actions/deploy-to-cloudflare-workers-with-wrangler) to deploy your application.

::note
Remember to [instruct Nitro to use the correct preset](/deploy#changing-the-deployment-preset). This is necessary for all presets, including `cloudflare_pages`.
::

## Environment Variables

Nitro allows you to universally access environment variables using `process.env`, `import.meta.env`, or the runtime config.

::note
Make sure to only access environment variables **within the event lifecycle** and not in global contexts, since Cloudflare only makes them available during the request lifecycle and not before.
::

**Example:** If you have set the `SECRET` and `NITRO_HELLO_THERE` environment variables, you can access them in the following ways:

```ts
import { defineHandler } from "nitro";
import { useRuntimeConfig } from "nitro/runtime-config";

console.log(process.env.SECRET) // note that this is in the global scope! so it doesn't actually work and the variable is undefined!

export default defineHandler((event) => {
  // note that all the below are valid ways of accessing the above mentioned variables
  useRuntimeConfig().helloThere
  useRuntimeConfig().secret
  process.env.NITRO_HELLO_THERE
  import.meta.env.SECRET
});
```

### Specify Variables in Development Mode

For development, you can use a `.env` or `.env.local` file to specify environment variables:

```ini
NITRO_HELLO_THERE="captain"
SECRET="top-secret"
```

::note
Make sure you add `.env` and `.env.local` to your `.gitignore` file so that you don't commit them, as they can contain sensitive information.
::

### Specify Variables for local previews

After building, when you try out your project locally with `wrangler dev` or `wrangler pages dev`, specify environment variables in a `.dev.vars` file in the root of your project (as described in the [Pages](https://developers.cloudflare.com/pages/functions/bindings/#interact-with-your-environment-variables-locally) and [Workers](https://developers.cloudflare.com/workers/configuration/environment-variables/#interact-with-environment-variables-locally) documentation).

If you are using a `.env` or `.env.local` file while developing, your `.dev.vars` should be identical to it.

::note
Make sure you add `.dev.vars` to your `.gitignore` file so that you don't commit it, as it can contain sensitive information.
::

### Specify Variables for Production

For production, use the Cloudflare dashboard or the [`wrangler secret`](https://developers.cloudflare.com/workers/wrangler/commands/#secret) command to set environment variables and secrets.

### Specify Variables using `wrangler.toml`/`wrangler.json`

You can specify a custom `wrangler.toml`/`wrangler.json` file and define vars inside.

::warning
This isn't recommended for sensitive data like secrets.
::

**Example:**

::code-group

```ini [wrangler.toml]
# Shared
[vars]
NITRO_HELLO_THERE="general"
SECRET="secret"

# Override values for `--env production` usage
[env.production.vars]
NITRO_HELLO_THERE="captain"
SECRET="top-secret"
```

```json [wrangler.json]
{
  "vars": {
    "NITRO_HELLO_THERE": "general",
    "SECRET": "secret"
  },
  "env": {
    "production": {
      "vars": {
        "NITRO_HELLO_THERE": "captain",
        "SECRET": "top-secret"
      }
    }
  }
}

```

::

## Direct access to Cloudflare bindings

Bindings let you interact with resources from the Cloudflare platform, such as key-value data storage ([KV](https://developers.cloudflare.com/kv/)) and serverless SQL databases ([D1](https://developers.cloudflare.com/d1/)).

::read-more
For more details on bindings and how to use them, refer to the Cloudflare [Pages](https://developers.cloudflare.com/pages/functions/bindings/) and [Workers](https://developers.cloudflare.com/workers/configuration/bindings/#bindings) documentation.
::

::tip
Nitro provides high-level APIs for primitives such as [KV Storage](/docs/storage) and [Database](/docs/database). Prefer them over depending directly on low-level platform APIs, for usage stability.
::

:read-more{title="Database Layer" to="/docs/database"}

:read-more{title="KV Storage" to="/docs/storage"}

At runtime, you can access bindings from the request event via `event.req.runtime.cloudflare.env`. For example, this is how you can access a D1 binding:

```ts
import { defineHandler } from "nitro";

defineHandler(async (event) => {
  const { env } = event.req.runtime.cloudflare
  const stmt = await env.MY_D1.prepare('SELECT id FROM table')
  const { results } = await stmt.all()
})
```

### Access to the bindings in local dev

In development mode, Nitro emulates the Cloudflare environment using [Miniflare](https://miniflare.dev/) (the same [`workerd`](https://github.com/cloudflare/workerd) runtime used by Wrangler and Cloudflare Workers in production). This means bindings are available natively from the request event, with no separate proxy or `wrangler` installation required.

The [`miniflare`](https://www.npmjs.com/package/miniflare) package is owned by your project: Nitro resolves it from your `node_modules` and offers to install it on first use.

To access bindings in dev mode, first define them. You can do this in a `wrangler.jsonc`/`wrangler.json`/`wrangler.toml` file:

::code-group

```ini [wrangler.toml]
[vars]
MY_VARIABLE="my-value"

[[kv_namespaces]]
binding = "MY_KV"
id = "xxx"
```

```json [wrangler.json]
{
  "vars": {
    "MY_VARIABLE": "my-value",
  },
  "kv_namespaces": [
    {
      "binding": "MY_KV",
      "id": "xxx"
    }
  ]
}
```

::

Alternatively, you can define bindings inline in your `nitro.config.ts` using the `cloudflare.wrangler` option (it accepts the same shape as `wrangler.json`):

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  preset: "cloudflare_module",
  cloudflare: {
    wrangler: {
      vars: {
        MY_VARIABLE: "my-value",
      },
      kv_namespaces: [{ binding: "MY_KV", id: "xxx" }],
    },
  },
})
```

From now on, when running

:pm-run{script="dev"}

you can access `MY_VARIABLE` and `MY_KV` from the request event as illustrated above.

#### Wrangler environments

If you have multiple Wrangler environments, you can specify which one to use during local dev emulation with the `cloudflare.wranglerEnv` option:

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  preset: 'cloudflare_module',
  cloudflare: {
    wranglerEnv: 'preview'
  }
})
```
