How to Troubleshoot Open-SEO Issues: A Complete Technical Guide for Cloudflare Workers and Docker Deployments

To troubleshoot Open-SEO issues, run a health check at /.well-known/health, inspect logs via wrangler tail (Workers) or docker compose logs (Docker), validate all required environment variables against .env.example, and verify database migrations are applied.

Open-SEO is a full-stack, self-hostable SEO debugging platform that deploys to either Cloudflare Workers (production default) or Docker containers for local environments. Understanding its layered architecture—from the edge runtime through the MCP server to workflow orchestrators—lets you pinpoint problems fast without guessing. This guide walks through diagnosing the most common Open-SEO troubleshooting scenarios using actual source paths and commands from the every-app/open-seo repository.

Understanding Open-SEO's Architecture Before Debugging

Every troubleshooting session starts with knowing which layer failed. Open-SEO's request flows through these components, each with distinct failure modes:

Layer Function Critical Source File
Edge runtime Serves public API via Cloudflare Worker or Docker container; routes to MCP server src/server/mcp/server.ts
MCP server Handles authentication, routing, validation; forwards to feature services src/server/mcp/transport.ts
Feature workflows Background jobs (rank-tracking, site-audit) using Drizzle ORM src/server/workflows/RankCheckWorkflow.ts
Database layer SQLite/D1 (Workers) or PostgreSQL (Docker) via shared Drizzle schemas drizzle.config.ts
Third-party integrations DataForSEO API, Google Search Console OAuth, Google Analytics 4 OAuth src/server/mcp/tools/search-console-tools.ts, google-analytics-tools.ts
Frontend Vite + React UI in web/ folder; talks to edge API via TanStack Query web/vite.config.ts
Telemetry/billing Svix webhooks; disable with OPENSEO_TELEMETRY_DISABLED=1 src/server/billing/svix.ts

When symptoms appear, map them to the correct layer first. A 401 error points to MCP server auth; missing rank data points to workflows or DataForSEO integration; frontend blank screens point to CORS or API connectivity.

Diagnosing Common Open-SEO Failure Points

401/403 API Authentication Errors

Symptom: API calls return unauthorized despite appearing to send credentials.

Root cause: Invalid or missing API key or session cookie in the Authorization: Bearer header.

Quick verification:

curl -H "Authorization: Bearer $OPENSEO_API_KEY" https://<your-host>/mcp/projects

Deep fix: Check src/server/mcp/transport.ts for the authentication middleware. The server validates Bearer tokens against stored credentials; ensure your key matches the environment where the worker runs (local, staging, or production). Cross-reference error responses with src/client/components/AuthConfigErrorCard.tsx for specific misconfigurations.

Health Endpoint Returns 500

Symptom: /.well-known/health fails with server error instead of {"status":"ok"}.

Root cause: Worker crash during startup—almost always missing required environment variables.

Critical variables to verify:

  • DATAFORSEO_API_KEY – required for rank-tracking and keyword research
  • GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET – required for Search Console/Analytics integration
  • DATABASE_URL (Docker) or D1 binding configuration (Workers)

Verification command:

curl https://<your-host>/.well-known/health

Docker-specific check:

docker compose exec open-seo curl -I http://localhost:1355/.well-known/health

For Docker deployments, consult docs/SELF_HOSTING_DOCKER.md#health-and-troubleshooting for the complete environment variable checklist.

Database Migration Failures

Symptom: Queries fail with "table not found" or schema mismatch errors; features appear to save data that disappears.

Root cause: Migrations not applied before service startup.

Docker resolution:

pnpm drizzle status          # Check pending migrations

pnpm drizzle up              # Apply migrations

Cloudflare Workers resolution:

wrangler d1 list             # Confirm D1 database binding

wrangler d1 execute <db-name> --file=./migrations/0000_initial.sql

The repository uses Drizzle ORM with database-agnostic schemas. The same drizzle.config.ts generates migrations for both SQLite/D1 (Workers) and PostgreSQL (Docker). Never skip the migration step when deploying fresh instances.

Rank-Tracking Workflow Stuck or Failing

Symptom: Keywords show "pending" indefinitely; scheduled checks never complete.

Root cause: Scheduler failure in src/server/features/rank-tracking/services/scheduledRankChecks.ts, typically from DataForSEO rate limiting (HTTP 429) or authentication errors.

Log inspection:


# Cloudflare Workers

wrangler tail --override

# Docker

docker compose logs open-seo | grep -i "rank\|dataforseo\|429"

Verification request:

import fetch from "node-fetch";

const API_KEY = process.env.OPENSEO_API_KEY!;
const projectId = "your_project_id";

async function diagnoseRankTracking() {
  const res = await fetch(
    `https://<your-host>/mcp/projects/${projectId}/keywords`,
    { headers: { Authorization: `Bearer ${API_KEY}` } }
  );
  
  if (!res.ok) {
    console.error("Rank API error:", res.status, await res.text());
    return;
  }
  
  const data = await res.json();
  console.log("Keyword statuses:", data.map((k: any) => ({
    keyword: k.keyword,
    lastChecked: k.lastCheckedAt,
    status: k.status
  })));
}

diagnoseRankTracking();

DataForSEO implements strict rate limits. If you see 429 responses, reduce check frequency or upgrade your DataForSEO plan. The RankCheckWorkflow.ts orchestrator queues jobs but cannot proceed when the upstream API rejects requests.

Missing Google Search Console Data

Symptom: Search Console reports show no data despite site ownership confirmed in Google's interface.

Root cause: Incomplete OAuth flow or insufficient OAuth scopes granted to the service account.

Re-authentication procedure:

npm run gsc:auth

Then verify in src/server/mcp/oauth-registration.ts that your callback URL matches exactly—protocol, port, and path. Mismatches cause silent failures where tokens never propagate to src/server/mcp/tools/search-console-tools.ts.

Frontend Cannot Reach API (CORS Errors)

Symptom: Browser console shows CORS policy errors; UI loads but data fetching fails.

Root cause: Origin not in allowedOriginHostnames list in src/server/mcp/transport.ts.

Verification:

curl -I https://<your-host>/mcp | grep -i "access-control-allow-origin"

Fix: Add your domain to the CORS configuration in transport.ts and redeploy. The default configuration permits localhost for development but requires explicit whitelisting for production domains.

Debugging Scripts and Commands

Automated Health Monitoring

// health-check.ts - Run in CI or monitoring systems
import fetch from "node-fetch";

async function checkHealth(baseUrl: string): Promise<void> {
  const res = await fetch(`${baseUrl}/.well-known/health`);
  
  if (!res.ok) {
    throw new Error(`Health check failed: HTTP ${res.status}`);
  }
  
  const json = await res.json() as { status: string };
  
  if (json.status !== "ok") {
    throw new Error(`Unhealthy status: ${json.status}`);
  }
  
  console.log("✅ Service healthy:", json);
}

// Usage
checkHealth(process.env.OPENSEO_URL || "http://localhost:1355");

Live Log Streaming

Cloudflare Workers:

wrangler tail --override --format=pretty

Typical error patterns to watch for:

  • ERROR src/server/mcp/transport.ts:115 – missing environment variable
  • ERROR src/server/workflows/RankCheckWorkflow.ts:NN – DataForSEO integration failure
  • WARN src/server/billing/svix.ts:NN – webhook delivery issues (non-critical if not using billing)

Docker:

docker compose logs -f open-seo --tail=100

Filter for your specific issue:

docker compose logs open-seo | grep -E "(ERROR|WARN|rank|mcp|database)" | tail -50

Database Connection Verification


# Docker PostgreSQL

docker compose exec open-seo psql $DATABASE_URL -c "SELECT COUNT(*) FROM projects;"

# Verify Drizzle schema synchronization

docker compose exec open-seo pnpm drizzle check

Environment Variable Reference for Troubleshooting

Variable Required For Validation Where Checked
DATAFORSEO_API_KEY Rank-tracking, keyword research, backlinks 32-character hex string src/server/mcp/tools/dataforseo-tools.ts
GOOGLE_OAUTH_CLIENT_ID Search Console, Analytics integration OAuth 2.0 client ID format src/server/mcp/oauth-registration.ts
GOOGLE_OAUTH_CLIENT_SECRET Search Console, Analytics integration OAuth 2.0 client secret src/server/mcp/oauth-registration.ts
DATABASE_URL Docker deployments only PostgreSQL connection string drizzle.config.ts, startup health check
OPENSEO_API_KEY API authentication User-generated in UI src/server/mcp/transport.ts auth middleware
OPENSEO_TELEMETRY_DISABLED Opt-out of telemetry 1 or unset src/server/billing/svix.ts
ALLOWED_ORIGINS CORS configuration Comma-separated hostnames src/server/mcp/transport.ts

Compare your actual environment against .env.example and .env.selfhost.example in the repository root. These template files document all optional and required variables with inline comments.

Summary

  • Start every troubleshooting session with GET /.well-known/health—200 with {"status":"ok"} confirms the edge runtime and MCP server started correctly.
  • Missing environment variables cause 500 errors on health checks and startup crashes; validate against .env.example before deep debugging.
  • Database migrations must be applied explicitly—pnpm drizzle up for Docker, wrangler d1 execute for Cloudflare Workers.
  • Rank-tracking failures usually indicate DataForSEO rate limits (429) or authentication errors; inspect logs with wrangler tail or docker compose logs.
  • CORS errors require adding your domain to allowedOriginHostnames in src/server/mcp/transport.ts.
  • Third-party OAuth flows (Google Search Console, Analytics) can silently fail—re-run npm run gsc:auth and verify callback URL matches exactly.

Frequently Asked Questions

How do I check if my Open-SEO instance is running correctly?

Send a GET request to /.well-known/health on your instance URL. A healthy response returns HTTP 200 with body {"status":"ok"}. If you receive 500, check logs immediately—this indicates a startup crash, typically from missing environment variables like DATAFORSEO_API_KEY or database connection failures.

Why is my rank-tracking data not updating?

The scheduler in src/server/features/rank-tracking/services/scheduledRankChecks.ts likely encountered a DataForSEO API error. Run wrangler tail (Workers) or docker compose logs (Docker) and search for "429" (rate limit) or authentication errors. Verify your DATAFORSEO_API_KEY is valid and has sufficient credits. You can also trigger a manual check via the MCP API to bypass the scheduler temporarily.

How do I fix CORS errors when connecting the frontend to my self-hosted Open-SEO API?

Add your frontend's exact origin—including protocol and port—to the allowedOriginHostnames array in src/server/mcp/transport.ts, then redeploy. The default configuration only permits localhost origins. Use curl -I https://<host>/mcp to verify the Access-Control-Allow-Origin header matches your domain before testing from the browser.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →