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 researchGOOGLE_OAUTH_CLIENT_IDandGOOGLE_OAUTH_CLIENT_SECRET– required for Search Console/Analytics integrationDATABASE_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 variableERROR src/server/workflows/RankCheckWorkflow.ts:NN– DataForSEO integration failureWARN 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.examplebefore deep debugging. - Database migrations must be applied explicitly—
pnpm drizzle upfor Docker,wrangler d1 executefor Cloudflare Workers. - Rank-tracking failures usually indicate DataForSEO rate limits (429) or authentication errors; inspect logs with
wrangler tailordocker compose logs. - CORS errors require adding your domain to
allowedOriginHostnamesinsrc/server/mcp/transport.ts. - Third-party OAuth flows (Google Search Console, Analytics) can silently fail—re-run
npm run gsc:authand 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →