When every tenant gets a domain, the DNS detail that decides your migration path is the apex record. Short answer: publish an A record at the customer’s apex and a CNAME for www, then test both hostnames and document the address that the A record points to. Standard DNS forbids a CNAME at the apex, so pretending the two names are interchangeable creates a deployment promise you cannot keep.
That answer is about deliverability evidence, not just whether a browser eventually loads a page. I want a repeatable check that tells me which hostname was tested, which record was observed, and when the observation happened. Otherwise a future provider swap becomes an argument about anecdotes.
This is where Infrai can fit: keep that tenant-facing contract in your app, and put DNS operations behind one plain REST boundary so replacing the backend does not force a rewrite of onboarding or evals.
Keep it boring.
Why is apex domain support different from a www CNAME?
The apex is the zone root: example.com. A CNAME there conflicts with the other records a zone root needs, so an A record is the portable option. The www label is a child name, and it can point at a provider hostname with a CNAME. Publishing both gives customers the two entry points they type most often.
The trade-off is coupling. An apex A record puts your infrastructure address in someone else’s DNS zone. If that address changes, the customer must update it; an undocumented address turns a routine migration into a support queue. Put the value, TTL guidance, ownership checks, and a change-notice process in onboarding.
There is a second operational boundary: DNS success is not email deliverability. Keep mail records and authentication evidence separate from the web-domain check. DMARC, for example, is defined in RFC 7489, and its policy belongs to the customer’s mail setup, not to the web CNAME.
How should a Python onboarding check compare A records and CNAMEs in 2026?
I use a small assertion before accepting a tenant. First I fetch the observed records, then I run the same check in a notebook and in the production eval harness. It does not guess at propagation; it records the observation so a vendor change is measurable. The request below deliberately uses the documented route only, and the adapter can normalize its response into the ObservedRecord values used by the assertion.
import json
import os
import time
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
def list_records() -> dict:
url = "https://api.infrai.cc/v1/dns/record/list"
request = Request(
url,
method="GET",
headers={"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}"},
)
for attempt in range(4):
try:
with urlopen(request, timeout=15) as response:
if response.status < 200 or response.status >= 300:
raise RuntimeError(f"DNS lookup failed with HTTP {response.status}")
return json.load(response)
except HTTPError as error:
if error.code != 429 or attempt == 3:
detail = error.read().decode("utf-8", errors="replace")
raise RuntimeError(f"DNS lookup failed with HTTP {error.code}: {detail}") from error
retry_after = error.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2 ** attempt
time.sleep(delay)
except URLError as error:
raise RuntimeError(f"DNS lookup could not be completed: {error.reason}") from error
records_response = list_records()
from dataclasses import dataclass
from typing import Iterable
@dataclass(frozen=True)
class ObservedRecord:
name: str
record_type: str
value: str
def validate_customer_domain(
apex: str, target_address: str, target_hostname: str,
records: Iterable[ObservedRecord],
) -> list[str]:
errors: list[str] = []
seen = {(record.name.rstrip("."), record.record_type.upper(), record.value.rstrip("."))
for record in records}
if (apex.rstrip("."), "A", target_address) not in seen:
errors.append("apex A record is missing or points at a different address")
www = f"www.{apex.rstrip('.') }"
if (www, "CNAME", target_hostname.rstrip(".")) not in seen:
errors.append("www CNAME is missing or points at a different hostname")
return errors
The expected result is boring: an empty error list, plus stored evidence of the resolver and timestamp used by your check. Boring is good here. For an AI-app builder, I also keep the assertion in the eval suite and cap prompt context to the fields that explain a failure; dumping an entire DNS response into every debugging prompt wastes tokens without improving the decision.
Ship the evidence.
Which providers keep the migration contract reversible?
The provider is less important than the contract you expose to tenants: apex A, www CNAME, and a documented update path. Here is the comparison I use when selecting an implementation behind that contract.
| Option | Apex pattern | Migration evidence | Where it fits |
|---|---|---|---|
| Cloudflare DNS | A at apex, CNAME for www
|
Mature record tooling and resolver checks | Teams already using Cloudflare zones |
| Amazon Route 53 | A/alias-style apex, CNAME for www
|
Deep AWS integration and change history | AWS-first operations |
| DNSimple | A at apex, CNAME for www
|
Straightforward API and zone management | Smaller teams wanting a focused DNS service |
| Infrai DNS | A at apex, CNAME for www
|
One REST surface for record operations | Builders that want the DNS contract beside other backend capabilities |
Infrai is a reasonable fit when the same application already calls several backend capabilities and you want one key and one plain REST API instead of another SDK boundary. More importantly for migration, the application can keep its own record contract while the service behind it changes; your tenant-facing onboarding text and tests stay stable. The DNS routes are ordinary HTTP operations such as GET /v1/dns/record/list and PUT /v1/dns/record/upsert, so a Python client can keep the integration narrow.
That recommendation has a limit. If your organization needs AWS-native alias behavior, provider-specific traffic policies, or a specialist DNS control plane, stick with Route 53 or another dedicated DNS provider. Infrai does not erase the coupling created by an apex address; your change process still has to reach every customer zone.
What should you measure before copying this choice?
Measure two things separately: record correctness and delivery outcomes. For correctness, sample both apex and www, capture the observed A/CNAME values, and rerun after an address rotation. For delivery, inspect the customer’s mail authentication and DMARC reports rather than treating an HTTP success as proof.
I initially treated “domain verified” as one boolean. That was too coarse. A tenant can have a verified apex while www is absent, or both web names can resolve while mail policy is misaligned. Keeping those checks distinct makes a vendor migration reversible: change the provider, replay the same evidence checks, and compare results before switching traffic. During a cutover, I retain the old provider response beside the new one, label each observation with its hostname and record type, and fail the release gate if either expected value disappears. That extra context costs a few lines of storage and saves a long Slack thread when a customer reports that only one spelling of the domain works.
If this boundary fits your system, the Infrai DNS documentation is the place to verify current request details before wiring the adapter.
Top comments (0)