Case study · 6 min read
Case study: shipping bizdev-hub.org on Cloudflare Workers, and what broke
This is our own site, built with the same stack and process we use for clients. We're publishing the problems along with the result, because how a team handles things going wrong tells you more than a clean success story.
Goal
A fast marketing site on our own domain with a working contact form, hosted at the edge, deployed in a repeatable way, with no servers to patch.
Stack
- Hosting: Cloudflare Workers with static assets; custom domains for the apex and
www. - Form backend: a Worker route that validates input, throttles per IP, and stores leads in D1 (SQLite at the edge).
- Deploys:
wrangler deployfrom a git repo, tagged on every release so any version can be redeployed.
Problem 1: the lead form returned error 1101
Symptom: after the first deploy, submitting the form returned Cloudflare's "error code: 1101" (Worker threw an exception).
Diagnosis: we streamed live logs with wrangler tail and read the exception: KV put() limit exceeded for the day. We had stored leads in Workers KV. On the free plan, KV allows 1,000 writes per day, counted across the whole account, and other projects on the account had already used it.
Fix: we moved lead storage to D1, whose free plan includes 100,000 rows written per day. Bonus: D1 is strongly consistent, so the "5 submissions per hour per IP" throttle is now accurate; KV's eventual consistency would have let bursts through. We also wrapped the database call so a storage failure returns a clear 503 instead of a crash.
Lesson: check platform quotas against the account's real usage before choosing a datastore, and make failure visible to the user rather than letting the platform's generic error page appear.
Problem 2: www did not redirect to the apex
Symptom: www.bizdev-hub.org returned 200 and served the page instead of redirecting.
Cause: by default, static assets are served directly and the Worker never runs for a path that matches a file.
Fix: set assets.run_worker_first: true so the Worker sees every request and can issue the 301. Verified afterwards: www now returns a 301 to the apex.
Problem 3: the new domain didn't resolve right away
Symptom: the deploy succeeded and Cloudflare reported both custom domains attached, but the apex failed to resolve on our network.
Cause: a resolver had already cached the "no record" answer from before the domain was attached (negative caching). Querying the authoritative nameserver showed the records were already live, and another public resolver (Google's 8.8.8.8) returned them.
Workaround for testing: call the site directly with curl --resolve bizdev-hub.org:443:<edge-ip> to prove the deployment, then wait out the cache. Don't conclude a deploy failed until you've asked the authoritative server.
Result
- Both domains served over HTTPS from Cloudflare's edge;
wwwredirects to the apex; unknown paths return a proper 404 page. - Contact form validated, throttled and storing leads; a smoke-test submission was stored in D1 and then deleted.
- Security headers on every response (
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Permissions-Policy). - Every release tagged in git, so rollback means redeploying a previous tag.
- Automated scanners were probing the site (requests for files like
/example.env) while we were still testing. There was nothing to find: the site serves only the assets we ship, and everything else 404s.
We haven't published customer metrics here because this page is about our own site. When we have client results we can share with permission, they'll appear in this section with the client's agreement.