Bali, Indonesia
Insights

Test the New Server With curl --resolve Before You Change Nameservers

November 3, 2026
0:00
A common migration order goes like this: copy the files, import the database, change the nameservers, then open the site and see what breaks. When something is wrong, like a blank PHP page, a redirect loop, or a certificate warning, real visitors are already landing on the new server while DNS caches slowly roll over. You don't have to find those problems that way. curl can send a real request to the new server, using your real domain name, before DNS points anywhere new. The option is --resolve. It tells curl, for this one command only, "when you look up this hostname on this port, use this IP address." Everything else about the request stays normal. That makes it one of the most useful pre-cutover checks in hosting support, and it takes a few seconds per URL.
The syntax is --resolve host:port:address. With --resolve example.com:443:203.0.113.10, curl skips the DNS lookup for example.com on port 443 and connects straight to 203.0.113.10. The URL you request still says https://example.com/, so the request looks the same as one from a real visitor:
  • The Host header is example.com, so the web server picks the right virtual host. On shared IPs this matters, because hitting the raw IP usually lands you on a default page instead of your site.
  • For HTTPS, curl sends example.com as the SNI name (Server Name Indication, the field that tells the server which certificate to present) and checks the certificate against example.com.
  • Cookies, redirects, and response headers all behave as they would for a visitor.
It does not touch your system resolver, your browser, or anyone else. Nothing is cached outside that one curl process, so there is nothing to clean up afterwards. That's the main advantage over editing your hosts file. A hosts entry works too, but it affects every program on the machine and is easy to forget, and browsers keep their own caches that can mislead you. The override is tied to a hostname and a port. An entry for port 443 does nothing for http:// requests on port 80, and an entry for example.com does nothing for www.example.com. You can repeat --resolve as many times as you need in a single command, and most of the surprises below come from forgetting one of those combinations. This checks the web side only. Whether your DNS zone on the new nameservers is complete, with MX, TXT, and subdomain records, is a separate job. That's covered in Pre-populating DNS Records on New Nameservers Before Registrar Cutover.
Before reading any status codes, make sure the request went where you think it did. In the examples, 203.0.113.10 is the new server and 198.51.100.20 is the old one. Replace them and the domain with your own.
BASH
# Verbose request to the new server; headers only, body discarded
curl -sv -o /dev/null --resolve example.com:443:203.0.113.10 https://example.com/

# Compact summary: status, final IP, redirect target, total time
curl -s -o /dev/null --resolve example.com:443:203.0.113.10 \
  -w '%{http_code} %{remote_ip} %{redirect_url} %{time_total}
' https://example.com/

# Same summary against the old server, for comparison
curl -s -o /dev/null --resolve example.com:443:198.51.100.20 \
  -w '%{http_code} %{remote_ip} %{redirect_url} %{time_total}
' https://example.com/

# Plain HTTP on port 80 needs its own override
curl -sI --resolve example.com:80:203.0.113.10 http://example.com/
In the verbose output, look for the line where curl adds your entry to its DNS cache and the line showing the IP it connected to. Here is roughly what that looks like:
TEXT
Illustrative output format (varies by curl version):
* Added example.com:443:203.0.113.10 to DNS cache
* Hostname example.com was found in DNS cache
*   Trying 203.0.113.10:443...
* Connected to example.com (203.0.113.10) port 443
* Server certificate:
*  subject: CN=example.com
*  issuer: ...
> GET / HTTP/2
> Host: example.com
< HTTP/2 200
< server: LiteSpeed
< content-type: text/html; charset=UTF-8
If the connected IP is not the new server, stop there. Usually there's a typo in the hostname or port, or the URL uses www while the override doesn't. The remote_ip field in the compact version gives you the same check in one line.
Running the same request against both IPs is the quickest way to spot a difference. A healthy migration usually gives the same status code, a similar set of headers, and the same redirect targets. When they differ, the difference usually points at the cause. A 500 on the new server with a 200 on the old one usually means PHP itself is failing: a missing extension, a different PHP version, or a database connection error because the new wp-config.php or .env still has old credentials. A 403 often means file ownership or permissions changed during the copy. A 404 on inner pages while the homepage works is the classic sign that rewrite rules didn't come along, for example an .htaccess that wasn't copied, or an Nginx server block without the try_files line the application expects. Headers tell you more than the status code. Compare server, x-powered-by (if present), content-type, cache-control, and any cache headers your stack adds. If the old server returns a PHP version header and the new one returns a different version, you've found a possible compatibility issue before any visitor hits it. Which headers appear depends on the server and its config, so treat missing ones as neutral, not as an error. Redirects need the most care. Without -L, curl shows you the Location header and stops, which is usually what you want here. Check that http:// redirects to https://, that www and the bare domain redirect the way they did before, and that the target is your real domain and not a temporary URL or IP left over from staging. That last one happens often after WordPress migrations where the siteurl and home values still point at a test address. If you do add -L to follow the chain, remember that each new hostname in the chain needs its own --resolve. Say example.com redirects to www.example.com and you only overrode the first. curl will resolve www through public DNS and quietly follow the redirect to the old server. The final result looks fine, and it tells you nothing about the new one. %{url_effective} and %{remote_ip} in the -w output show you where the chain ended.
This is where people panic for no reason, or ignore a real problem. Many hosts issue certificates automatically using HTTP validation. The certificate authority fetches a file from your domain over public DNS, and before cutover public DNS still points at the old server. So the new server often can't get a valid certificate for your domain yet. curl will then report a hostname mismatch or a self-signed certificate. That's not necessarily a migration bug, but you should know which situation you're in:
  • If the new server presents the hostname certificate of the server itself, or a self-signed one, no certificate for your domain has been issued yet. Plan for issuance right after cutover, or use DNS-based validation, or install a copy of your existing certificate on the new server if you are allowed to.
  • If it presents a certificate for a different customer domain, the vhost or SNI mapping is wrong. Fix that before cutover.
  • If it presents your certificate but www or mail is missing from it, the coverage gap will follow you to the new host. AutoSSL Skips the www or mail Subdomain? Check Where Its DNS Actually Points explains how to read that.
To test PHP and rewrites while the certificate is still pending, you can add -k (--insecure) so curl skips certificate verification. Only use it for that. It hides exactly the problem you would otherwise be checking, so drop it once the real certificate is in place and run the request again.
The homepage alone proves little. Most sites are mostly cached HTML on the homepage, while the problems show up on a login page, a search result, a checkout step, or an old URL that depends on a redirect rule. Put a handful of important paths in a file, one per line (/, /wp-login.php, /contact/, a product page, an old URL that should redirect), and run them against both hostnames:
BASH
#!/usr/bin/env bash
# Read-only check: prints status, host, path and redirect target for each URL
NEW_IP='203.0.113.10'
while read -r path; do
  for host in example.com www.example.com; do
    result=$(curl -s -o /dev/null --max-time 15 \
      --resolve "host:443:NEW_IP" \
      -w '%{http_code} %{redirect_url}' \
      "https://hostpath")
    printf '%s  %s%s
' "result" "host" "$path"
  done
done < paths.txt
Run it once with the new IP and once with the old IP, and compare the two outputs side by side (diff works well). Any line that differs is worth a closer look with curl -sv. --max-time keeps a stuck PHP request from hanging the whole loop, and a 000 status means curl couldn't complete the request at all. That usually means a firewall, a missing listener on that port, or the timeout. There's one blind spot. --resolve only affects your curl command. If the application on the new server calls its own domain, as WordPress does for loopback and scheduled tasks, that request goes through the new server's resolver, which still sees the old IP. Page loads can look perfect while background jobs quietly hit the old server. Whether that matters depends on the application, but keep it in mind if something works in your tests and fails after cutover. The practical habit: before changing nameservers, check that every important URL returns from the new IP with the status, headers, and redirect targets you expect, for both example.com and www, over both HTTP and HTTPS. Know whether any certificate error is expected or a real mapping problem. It takes a few minutes, needs nothing but curl, and means the only thing left to wait for after the nameserver change is DNS caching itself.