DEV Community

orca forge
orca forge

Posted on Originally published at forge.workstyle.tech

Why nginx Shows Another Site After a Domain Migration

📝 Originally published (in Japanese) at forge.workstyle.tech.

TL;DR: Old DNS cache can send requests for a moved domain to the old server. When no vhost matches and nginx has no default_server, the first loaded vhost can serve its site and certificate. A server_name _ default server can reject HTTP with return 444 and HTTPS with ssl_reject_handshake on.

TL;DR

On devices with stale DNS cache (old connection information), there was no matching vhost, so the site and certificate from the first vhost loaded could be returned. The cause was that nginx had no default_server. I added a default server with server_name _, rejecting HTTP with return 444 and HTTPS with ssl_reject_handshake on.

Why Did the Internal Wiki Appear on the Old Domain?

During work from 2026-10-13 to 15, I ran into a situation where I thought I was opening the domain after migration, but instead saw a different app on the old server. I was also seeing a certificate error. I had assumed the migration would be complete once DNS switched over, so this was unexpected.

This happened on a device with a stale DNS cache. DNS looks up the destination associated with a domain name, and when a cached entry remains, the previous destination may still be used. Opening the old domain sent the request to the old server, where the intended vhost had already been removed.

At first, I thought the configuration for the deleted vhost might still be there. But when I checked the records, the relevant vhost was already gone. The problem was that there was no configuration to handle the request afterward.

This server also hosts other apps. In nginx, setting default_server determines where requests that don’t match any vhost are sent. If it isn’t set, the first vhost loaded becomes the default, and its configuration handles requests that don’t match another vhost. The certificate for that same vhost is returned, too.

As a result, requests to the old domain reached another app’s login page. The certificate error occurred because the server returned a certificate from a different vhost. If the old server may still respond after a domain migration, deleting the relevant vhost alone doesn’t determine where those requests will go.

Rejecting HTTP and HTTPS requests to nginx’s default server

I added a default server with server_name _. For HTTP, I set return 444; for HTTPS, I set ssl_reject_handshake on. The HTTPS handshake is the connection setup that happens before encrypted communication begins.

The key parts of the configuration are:

server_name _;
return 444;
Enter fullscreen mode Exit fullscreen mode

For HTTPS, I used:

server_name _;
ssl_reject_handshake on;
Enter fullscreen mode Exit fullscreen mode

ssl_reject_handshake is available in nginx 1.19.4 and later. This configuration does not require a certificate. Instead of accepting the HTTPS connection and returning another site’s certificate, it rejects the handshake.

After adding this configuration, existing sites were unaffected, and all certbot dry runs (checks of the certificate renewal process) succeeded.

Did We Figure Out Why a Record with a 3600-Second TTL Stayed for 4432 Seconds?

During the investigation, a record with a 3600-second TTL remained on my Mac for 4432 seconds. A TTL is the length of time DNS information may be cached, so the record appeared to have stayed longer than that.

I haven’t confirmed why it lasted that long, so I can’t attribute it to any particular caching mechanism. What I did learn is that when stale information remains on a device, requests may continue reaching the old server for a while after a migration.

What I Learned After Removing a vhost

The issue wasn’t the removal of the old vhost itself. Because I hadn’t set up a default server, requests with no matching destination were routed to the first vhost nginx loaded.

When hosting multiple sites on the same nginx instance, you need to decide how to handle requests that don’t match any vhost, as well as configure the vhosts you use. I added a default server with server_name _, configured it to return 444 for HTTP, and set ssl_reject_handshake on for HTTPS.

I don’t know why old DNS cache entries for the previous domain persist, or for how long, but I can provide a fallback that keeps stale requests from showing a different app. I’ll make sure to check the old server’s default behavior after future migrations.

Key takeaways

  • Check whether requests for the old domain reach the old server.
  • Check whether the relevant vhost remains and how nginx handles unmatched requests.
  • Configure server_name _ with HTTP return 444 and HTTPS ssl_reject_handshake on.
  • Verify existing sites and the certbot dry-run.
  • The cause of the TTL 3600-second record remaining for 4432 seconds is unconfirmed.

Top comments (0)