Skip to content
Back to all posts

The Domain Moved. Some Requests Did Not.

By Maksym Kuzmitskyi
7 min read
The Domain Moved. Some Requests Did Not.

The new domain works on your laptop. The certificate is valid. The application responds.

Someone else is still reaching the old server.

There is usually nothing particularly mysterious about this. The system has several caches, several connections, and several ideas about what "the address" means. They do not update together because we changed a record in a dashboard.

In the first part of this guide, I followed a request from the browser into the backend. Here I want to stay at the entrance, where fairly small configuration changes can leave an application working for one group of users and quietly wrong for another.

A DNS change has a transition period

Lowering a record's TTL immediately before changing its value does not shorten answers already cached under the previous TTL. A resolver that cached an hour-long answer ten minutes ago can keep it for the remaining fifty minutes.

I would lower the TTL ahead of a planned cutover, wait for the old lifetime to pass, and keep both destinations usable during the move. DNS TTL is a cache instruction, not a synchronized release mechanism.

There is another easy-to-miss cache: the absence of a record. If someone queried a hostname before it existed, their resolver may retain a negative answer. Creating the record does not necessarily make that user able to resolve it immediately.

Existing HTTP connections can also outlive a DNS update. A client does not need to resolve the name again for every request on an already-open connection.

None of this is a reason to avoid DNS cutovers. It is a reason not to delete the old destination the moment your own lookup looks correct.

Check both address families

A hostname can publish both an A record for IPv4 and an AAAA record for IPv6.

If the IPv4 route is healthy while IPv6 reaches an old endpoint, the result may depend on the user's network. Browsers have strategies for choosing between address families, but those strategies cannot turn a wrong AAAA record into the right infrastructure.

The symptom is frustratingly ordinary: "It works here."

These are diagnostic commands, not a deployment script:

dig +short A api.shop.example dig +short AAAA api.shop.example curl -4 -I https://api.shop.example/health curl -6 -I https://api.shop.example/health

The last command requires a working IPv6 path from the machine running it. Failure there is evidence to interpret, not automatic proof that the server is broken.

I would also inspect the authoritative records and compare them with the resolver used by an affected client. "Propagation" is often used as a name for any uncertainty around DNS. It is more useful to identify which answer is cached where.

The certificate belongs to a connection, not a diagram

With a CDN in front of a load balancer, there may be two TLS connections: browser to CDN, and CDN to origin. Each has its own certificate validation and hostname expectations.

A valid browser-facing certificate says nothing about whether the CDN can validate the origin certificate. The origin address, TLS server name, certificate names, and HTTP Host header have related but distinct jobs.

Suppose the origin certificate covers origin.shop.example, while the CDN connects using a load balancer's generated hostname. Depending on the configured origin behavior, verification can fail before the application sees a request.

I would debug the two connections separately rather than temporarily disable origin verification. The latter makes the immediate error disappear by removing the property we were trying to establish.

Renewal is the configuration test that arrives later

A certificate issued successfully today still needs an automated renewal path.

An HTTP-01 challenge requires the validation resource to be reachable through the public route. Add authentication, a new redirect, or a different proxy rule later, and renewal may fail while ordinary application requests continue working.

DNS-01 avoids that HTTP path and supports wildcard certificates, but introduces credentials and DNS update behavior into the renewal system. Give the renewal process narrowly scoped access rather than the registrar's unrestricted credentials. Challenge delegation can help separate that responsibility.

The Let's Encrypt challenge documentation explains these trade-offs. A wildcard for *.shop.example also does not cover the apex shop.example; certificate names deserve the same explicit review as routing rules.

I would monitor expiry from outside the application and exercise renewal before the certificate becomes urgent. A calendar reminder is not quite the automation people usually mean when they say HTTPS is handled.

The proxy changes what the application believes

Behind TLS termination, the application may receive plain HTTP from the load balancer. Without trusted forwarding information, it can generate HTTP callback URLs, fail to set secure cookies, or redirect a request that is already HTTPS into a loop.

Forwarded protocol, host, and client-address headers provide that context. They are not trustworthy merely because they have conventional names.

An internet client can supply X-Forwarded-For too. The proxy must remove or normalize incoming values according to the topology, and the application must trust only the known proxy path. Blindly trusting every hop makes client-IP rate limits and audit records easier to mislead.

For password-reset and OAuth URLs, I prefer a configured, validated public origin rather than constructing an absolute URL from an arbitrary Host header. It is less flexible. That is sometimes the point.

Public address, transport address, and trusted request context are separate configuration decisions.

Moving the hostname can change authentication

A host-only cookie for shop.example is not sent to api.shop.example. A cookie scoped to the parent domain has a broader reach, which can be undesirable if another subdomain is less trusted.

Also, two subdomains can be same-site while still being different origins. SameSite cookie rules and CORS answer different questions. Cross-origin browser requests still need the right credential mode and CORS response; a credentialed response cannot use a wildcard allowed origin.

The distinction becomes visible after a seemingly harmless move from /api on the main origin to a separate API hostname. Authentication, preflight requests, callback allowlists, and browser storage behavior may all change.

I would test an existing session, not just a fresh login. I would test logout too. Deleting a cookie with the wrong path or domain can leave the original cookie present, which is a fairly unhelpful way to discover how cookie scope works.

Preserve request semantics through redirects

A canonical-domain redirect is fine for a public page. Applying the same rule indiscriminately to API writes is less comfortable.

Some redirect statuses can lead clients to change a POST into a GET. Others preserve the method and body. Even a method-preserving redirect can encounter credential and origin rules that an ordinary page navigation does not.

For a moved API, I would keep the old route compatible during the transition rather than expect every client to follow a redirect correctly. The client might be a browser tab, an SDK, or a webhook sender that cannot be upgraded today.

Permanent redirects can be cached too. A rollback of DNS or application code does not necessarily rewind the redirect already stored by a client.

Test an address change as a release

A useful cutover check includes more than a homepage returning 200: authenticated reads, writes, OAuth callbacks, uploads, streaming responses, certificate renewal, and the old endpoint during overlap.

Proxy buffering is worth checking if the application streams. A server can emit chunks correctly while a proxy collects them and sends everything at the end. The request technically works; the intended latency behavior does not.

I would keep the old endpoint alive until observed traffic has moved, then remove it deliberately. The decision comes from request logs and compatibility requirements, not the moment the DNS control panel says the record was saved.

The domain is a small part of the application, but it has a long memory. Caches, cookies, certificates, and old clients all remember slightly different versions of it. A good move gives those memories time to become harmless.

Next comes container and environment configuration, where the application can have the right address and still be running the wrong settings.

If a domain move left one awkward client behind, I'd be interested in which layer retained the old assumption. Those details are more useful than another checklist that stops at a green padlock.

About the author

Maksym Kuzmitskyi is a Senior Software Developer focused on React and TypeScript. His work includes enterprise applications, shared frontend components, Node.js integration, testing and accessibility. Alongside frontend work, he designs AI-assisted delivery workflows and has developed personal review tooling.

Telegram

More than a blog post

I share frontend news and the reasoning behind it throughout the day. Pick the language that feels natural to you.

Need to discuss your project? Get in touch.