Skip to main content

Self-Hosted Middleware: Prerender Without Changing Your DNS

Most Hado SEO setups point your DNS at our proxy. If you’d rather keep your own infrastructure — your CDN, your Worker, your routing — you can run a thin Cloudflare Worker in front of your site that pre-filters bot traffic and calls the Hado SEO render endpoint for prerendered HTML.
Advanced / self-hosted. This guide is for teams that want to integrate at the edge themselves. If you just want SEO with zero code, use the DNS setup instead.

How it works

Your Worker is a thin, untrusted client. All the heavy lifting — authentication, domain authorization, authoritative bot verification, caching, and rendering — lives in the Hado SEO service. Your Worker only has to:
  1. Decide if a request looks like a bot (a cheap User-Agent check).
  2. For bots, ask the Hado SEO render endpoint for prerendered HTML.
  3. Serve that HTML on 200, or serve your app normally on 204 (passthrough).
Your bot check can be loose. Hado SEO re-verifies every bot authoritatively (User-Agent + published IP ranges), so a human or spoofed crawler that slips through simply gets a 204 and is served your normal app. False positives only cost one subrequest.

Prerequisites

  • Your site’s domain is on Cloudflare (the zone is active and serving your traffic) — the Worker runs on your zone, in front of your existing app.
  • Node 18+ locally, for the wrangler CLI. (Everything below can also be done in the Cloudflare dashboard UI; the CLI path is shown because it’s exactly reproducible.)

Setup

1

Add your domain in Hado SEO

Sign up at hadoseo.com/auth. In onboarding, when asked “How are you connecting?”, choose Cloudflare Worker. Enter your custom domain — that’s all; unlike the DNS path, no app URL is needed. This registers domain ownership: the render endpoint only serves domains your account owns.
2

Copy your API key

The onboarding success screen issues an API key (hado_sk_...) and shows it once — copy it now. If you already closed it, create a new key in Settings → API Keys. See Authentication.
3

Copy your render endpoint

From the same screen (or the dashboard’s domain page):
4

Create the Worker project

The fastest path is cloning the ready-made template — Worker, config, and deploy scripts included:
Or build it by hand — a fresh directory with three files is all it takes:
Save the Worker below as src/worker.js, and add:
wrangler.toml
Replace example.com with your domain in both patterns and zone_name.
5

Set your API key as a secret

The key is a secret, never a [vars] entry — vars are visible in the dashboard and in wrangler.toml.
6

Deploy

Deploying attaches the routes: from this moment every request to your domain passes through the Worker. Humans are completely unaffected (one header check, then your app) — there is no cutover moment to schedule.
7

Verify

Three checks, in order:
  1. Open your Hado SEO dashboard → Analytics: the test crawls appear within a few minutes. Then run the SEO Bot Crawler Test against a page to confirm real crawlers receive fully rendered HTML.

The Worker

This is the same src/worker.js shipped in the hado-cloudflare-worker template — clone that instead of copy-pasting if you want the wrangler config and deploy scripts along with it.
src/worker.js

What the Worker sends

GET {HADO_RENDER_ENDPOINT}?url=<absolute request URL> with: The Worker also fires a fire-and-forget POST {HADO_RENDER_ENDPOINT origin}/v1/event/referral beacon for human page navigations that carry a Referer, with body {"url": "<landing URL>", "referer": "<referer>"} and the same auth. This is how visits arriving from AI assistants (ChatGPT, Perplexity, Claude, …) show up in your AI-referral analytics — no IP is ever sent on this path. It always returns 204; failures are silently ignored and never affect the visitor.
Always forward the visitor’s user-agent, and copy the visitor’s IP into x-hadoseo-client-ip — never your Worker’s own values. Hado SEO verifies bots by matching the UA against published crawler IP ranges — if the IP that arrives is your Worker’s egress, real crawlers will be rejected as spoofed and served a 204.

How to handle the response

The service fails open: on any internal error or timeout it returns 204 rather than a 5xx, so your site never breaks. Your Worker should mirror that — fall through to fetch(request) on anything that isn’t a 200, a 404 snapshot, or a redirect.
Routing rules & blocked paths. Redirect rules from your dashboard are returned as real 30x responses so bots see them. Proxy rules and blocked paths come back as 204 — your own edge serves those paths (Hado never prerenders them), and blocked-path robots directives arrive in x-hadoseo-robots for you to forward. Rule changes take effect within about a minute (the service caches your domain config briefly).

Redirects for human visitors

The Worker only routes bot traffic through Hado SEO, so your dashboard’s redirect rules apply to crawlers — which is what consolidates link equity and keeps search engines pointed at the right URLs. Human visitors go straight to your app, untouched, and that’s deliberate: it keeps the Worker from adding any latency to real users. For humans, configure the same redirects where they’re free and instant on your own zone: Cloudflare Redirect Rules (or Bulk Redirects for long lists) under Rules in your Cloudflare dashboard, or in your app’s own routing. When you move a page, set the redirect in both places — your zone rule covers visitors, your Hado rule guarantees crawlers see the 301 even before caches turn over.

Analytics-only (passthrough mode)

Only want crawl and AI-agent analytics, without serving prerendered HTML? Send the x-hadoseo-mode: passthrough header on every request. Hado SEO records who crawled what (feeding your crawl analytics) and returns 204, so your app is always served as-is.
Because passthrough never renders, it uses no render quota — only your per-minute rate limit applies. Passthrough mode still populates the snapshot timeline: when a bot crawls a page, Hado fetches that page from your origin (at most once per URL per day) and archives what it served. These appear in the page drawer with an origin label — “what your origin served near crawl time” — alongside your crawl analytics, with no extra setup and no load beyond one background fetch per page per day.

Caching repeat bot hits (optional)

Every 200 comes back with Cache-Control: public, s-maxage=… , stale-while-revalidate=…. If you serve the Worker response from Cloudflare’s cache, repeat bot hits for the same URL are served from your edge and never reach Hado SEO. The simplest approach is to store the render in the Worker’s caches.default, keyed by the request URL, and revalidate in the background with ctx.waitUntil.

How Hado SEO Works

Understand caching, bot verification, and the render pipeline behind the endpoint.