# How to Self-Host OpenSEO on Cloudflare Workers: Complete Deployment Guide

> Learn to self-host OpenSEO on Cloudflare Workers. Deploy your own instance with KV storage and built-in authentication for complete control. Get the guide now.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-08-05

---

**You can self-host OpenSEO entirely on Cloudflare's edge platform by creating a `.env.selfhost` configuration, running the pre-flight validation script, and deploying with `pnpm deploy:selfhost --yes` to create a Worker with KV storage and built-in Access authentication.**

OpenSEO by every-app/open-seo is designed to run as a Cloudflare Worker, leveraging edge computing for SEO analysis. This guide walks you through the exact steps to deploy your own instance using the repository's built-in self-hosting toolchain.

## Prepare Your Environment Configuration

Begin by creating your environment file from the provided template. Copy `/.env.selfhost.example` to `.env.selfhost` and configure the required variables:

- **`TEAM_DOMAIN`**: Your Cloudflare Access domain (e.g., `https://your-team.cloudflareaccess.com`)
- **`POLICY_AUD`**: The Access application audience ID for auto-provisioning
- **`DATAFORSEO_API_KEY`**: Required for third-party SEO data services
- **`ACCESS_ALLOWED_EMAILS`**: Comma-separated list of collaborator emails

To disable telemetry reporting, set either `OPENSEO_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK=1` in your environment file.

## Validate Configuration with Pre-Flight Checks

Before deploying, run the validation script located at [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts). This script performs critical checks:

- Validates the `TEAM_DOMAIN` format using the regex in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts)
- Verifies Cloudflare Access domain reachability
- Confirms required secrets like `DATAFORSEO_API_KEY` are present

The pre-flight check aborts early if misconfigurations are detected, preventing failed deployments. The validation logic ensures your domain matches the pattern `^https?:\/\/.+\.cloudflareaccess\.com$` as implemented in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts):

```typescript
export function validateTeamDomain(input: string) {
  const trimmed = input.trim();
  if (!/^https?:\/\/.+\.cloudflareaccess\.com$/.test(trimmed)) {
    return { ok: false, error: "add the https:// prefix to ..." };
  }
  return { ok: true, origin: trimmed };
}

```

## Deploy the Worker Stack

Execute the deployment command from the repository root:

```bash
pnpm install
pnpm deploy:selfhost --yes

```

This command triggers the Vite build process configured in [`vite.config.ts`](https://github.com/every-app/open-seo/blob/main/vite.config.ts), which uses the Cloudflare Vite plugin to bundle your TypeScript into a single Worker:

```typescript
import { defineConfig } from "vite";
import { cloudflare } from "@cloudflare/vite-plugin";

export default defineConfig({
  plugins: [
    cloudflare({
      inspectorPort: false,
      viteEnvironment: { name: "ssr" },
    }),
  ],
});

```

The deployment automatically provisions:
- A Workers AI model binding via `@cloudflare`
- A Workers KV namespace for persisting user data
- A Cloudflare Access application with an "Allow" policy (if `TEAM_DOMAIN` is provided)

The Worker entry point at [`src/start.ts`](https://github.com/every-app/open-seo/blob/main/src/start.ts) initializes the application:

```typescript
import { env } from "cloudflare:workers";
import { initApp } from "./app";

export default {
  async fetch(request, env) {
    const app = initApp(env);
    return app.handle(request);
  },
};

```

## Post-Deployment Access Configuration

After deployment, complete the authentication setup:

1. Add collaborators to the Access policy via `ACCESS_ALLOWED_EMAILS` in `.env.selfhost`
2. If you opted out of auto-provisioning, manually configure your Cloudflare Access application in the dashboard
3. Test the deployment by visiting `https://<your-worker>.workers.dev/api/heartbeat`

The middleware in [`src/middleware/ensure-user/cloudflareAccess.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/ensure-user/cloudflareAccess.ts) enforces Access authentication on protected routes.

## Why Cloudflare Workers?

**Edge Execution** places SEO crawling and analysis jobs geographically close to target sites, reducing latency. **Built-in KV & AI** provides serverless storage via Workers KV and AI-assisted features through Workers AI without external servers. **Zero-Ops Scaling** allows the Worker to automatically handle traffic spikes during intensive crawling or rank-checking workloads.

## Common Pitfalls and Troubleshooting

| Issue | Symptom | Fix |
|-------|---------|-----|
| **Access login failures** | Users cannot authenticate via Cloudflare Access | Verify emails are listed in `ACCESS_ALLOWED_EMAILS` or check manual Access policy configuration |
| **Missing API keys** | DataForSEO returns 401 errors | Ensure `DATAFORSEO_API_KEY` is defined in `.env.selfhost`; the pre-flight script flags missing keys |
| **Telemetry enabled** | Logs show telemetry transmission | Set `OPENSEO_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK=1` and redeploy |

## Summary

- Create `.env.selfhost` from the example template, setting `TEAM_DOMAIN` and required API keys
- Run [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts) to validate your Cloudflare Access configuration before deploying
- Deploy using `pnpm deploy:selfhost --yes`, which bundles via Vite and provisions KV and AI bindings
- Manage collaborator access through `ACCESS_ALLOWED_EMAILS` or the Cloudflare dashboard
- Disable telemetry with `OPENSEO_TELEMETRY_DISABLED=1` if desired

## Frequently Asked Questions

### What environment variables are required to self-host OpenSEO on Cloudflare Workers?

You must configure `TEAM_DOMAIN` for Cloudflare Access integration, `DATAFORSEO_API_KEY` for SEO data services, and `ACCESS_ALLOWED_EMAILS` for user authentication. The `/.env.selfhost.example` file provides the complete template with optional variables like `POLICY_AUD` for automatic Access application provisioning.

### How does the pre-flight check validate my Cloudflare Access configuration?

The script [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts) imports `validateTeamDomain` from [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts) to verify your `TEAM_DOMAIN` matches the required Cloudflare Access URL pattern. It also checks domain reachability and confirms all required secrets are present in your `.env.selfhost` file.

### Can I disable telemetry when self-hosting OpenSEO?

Yes. Set `OPENSEO_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK=1` in your `.env.selfhost` file before deploying. The telemetry logic in [`src/server/lib/self-host-telemetry.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/self-host-telemetry.ts) checks these variables and skips reporting when either flag is enabled.

### How do I update my OpenSEO Worker after changing environment variables?

Simply rerun `pnpm deploy:selfhost --yes` from the repository root. The deployment script detects the `selfhost` stage and applies updated configuration values from `.env.selfhost` to your Cloudflare Worker.