We queued ten domains in the admin console, watched the queue accept all ten, and after several minutes zero of the ten were registered at Cloudflare. The console had a clean button. Click it, and each domain got queued for Cloudflare custom-hostname registration. A worker drained the queue and did the real registration over the API, so no token sat in the browser.

So we went looking, and the worker’s create path turned out to be literally stamped “NOT YET IMPLEMENTED” in the tree. The button had been writing queue files into a void. The 26 domains already live on Cloudflare must have been registered directly against the API, because nothing else could have created them. Nobody had ever wired the drain that was supposed to do it automatically.

The setup that looked airtight

We were migrating a batch of domains onto Cloudflare. The www hostname for each one becomes a Cloudflare SaaS custom hostname. You CNAME the www record at the Cloudflare edge, Cloudflare HTTP-validates it, and it issues a DV cert in about five minutes. We had already done 26 of these. The remaining 10 were “not started” in the tracking, so we reached for the console that was built to do exactly this.

The documented design was good. The console writes one queue file per domain into a queue directory. A separate PowerShell worker drains that queue and calls the Cloudflare API to register each custom hostname. Keeping the API token on the worker box and out of the browser is the right call. The abstraction was clean enough that we trusted it on sight, and that was the problem.

A clean abstraction tells you what is supposed to happen. It says nothing about whether it happened. The console’s job ended when it wrote the queue file. From the browser’s side, ten successful writes look identical whether the worker behind them is a finished daemon or a stub that never runs.

Asking Cloudflare directly

After the first couple of minutes we stopped waiting on the queue directory. A file landing there tells you nothing about whether a hostname exists at Cloudflare. The effect lives in exactly one authoritative place, the Cloudflare API.

So we polled it, read-only, for all ten hostnames. We skipped the console’s view of the queue and asked the actual zone whether these custom hostnames existed. Zero. After minutes of “draining,” nothing had been created on the other end.

That gap between “we triggered it” and “it happened” is where a lot of automation rots. A button click, a queued job, an enqueued message, a 202 Accepted: each one is a promise, not a receipt. The only confirmation is to ask the downstream system whether the thing you wanted now exists. Polling the API for ten hostnames took less effort than the speculation we would otherwise have burned, and it turned a vague “is this slow?” into a hard “this never ran.”

The worker was never built

Once the API said zero, the next move was to read the worker. Its create path was stamped “NOT YET IMPLEMENTED.” There was no race, no token problem, no slow drain. The drain didn’t exist. The queue was a TODO with extra steps and a UI on top.

That left the question of how the other 26 domains got live. They couldn’t have come through this worker. The only path left is that they were registered directly against the API, and the console’s tidy queue-and-worker story had grown up around a process that was carrying the whole load by hand. The abstraction described an intent that had never shipped.

We keep running into this flavor of stub, the kind that returns a plausible non-result and gets papered over by a UI. It’s a cousin of a billing path we hit the same week, where a downgrade-confirmation dialog was just a stub that routed to “email support.” That was a transition nobody built, sitting on the happy path and looking like working code. A stub that returns something is more dangerous than one that throws, because the something flows downstream and nobody notices the hole until an unexpected input falls through it.

Copying the config off a live hostname

We had two options. We could wait for someone to implement the worker’s create path, which is open-ended work on a path we now knew was fiction. Or we could do what the 26 working domains had clearly done already, and register the hostnames directly using a config we knew worked.

We took the second one. We didn’t rebuild the registration config from the docs, since the docs are what produced the unimplemented worker. Instead we pulled the actual settings off an existing live hostname, one of the 26 already serving traffic correctly, and copied them verbatim:

ssl: { method: http, type: dv, settings: { min_tls_version: "1.2" } }
custom_origin_server: <your-cf-origin-hostname>

That config wasn’t a guess. It came from a domain Cloudflare was already validating and serving. When the abstraction you were handed is vapor, the most reliable spec is a running instance of the thing you’re trying to reproduce. A known-good instance is documentation that can’t drift, because it is in production proving itself.

We registered all ten against the API with that config. Each came back pending, which is the correct state: Cloudflare knows about the hostname and is waiting for DNS validation. Then we flipped the www CNAMEs so Cloudflare could HTTP-validate and issue the certs, on the same roughly five-minute cadence the 26 prior domains had taken. We left every apex untouched on the origin. The www path and the bare-apex path are entirely different problems, and we weren’t going to conflate them.

Checking the far end again

We used the same discipline at the finish as at the start. Within minutes, 8 of the 10 were active. Smoke tests on those returned 200 with server: cloudflare in the headers. That is the receipt. An independent request to the hostname comes back served by Cloudflare, which says more than “we registered it.” The remaining two were mid-validation on the normal timeline and not stuck.

A registration call returning success is still only a trigger. The hostname serving a 200 through Cloudflare is the effect. When automation should have run, check the effect in the system that owns it, read-only and directly, and believe that answer over the button pressed or the queue filled. And when the documented path turns out to be unimplemented, copy the config off a known-good production instance.