[skipdns.dev]

curl --resolve: Test a Site on a New Server Before DNS

Last updated · 4 min read

When you move a site to a new server, you want to test it under its real domain before you change DNS. curl --resolve does that for a single command. It tells curl "for this hostname and port, connect to this IP", while everything else (the URL, the Host header, TLS SNI, certificate checks) still uses the real domain. Nothing on your system changes, and there's nothing to undo afterwards.

The basic command

The format is --resolve HOST:PORT:ADDRESS. The port must match the URL's port: 443 for HTTPS, 80 for HTTP.

# HTTPS
curl --resolve example.com:443:203.0.113.10 https://example.com/

# HTTP
curl --resolve example.com:80:203.0.113.10 http://example.com/

Add -I for headers only, or -v to see where curl actually connected:

curl -sv -o /dev/null --resolve example.com:443:203.0.113.10 https://example.com/ 2>&1 | grep -E "Added|Connected|HTTP/"

* Added example.com:443:203.0.113.10 to DNS cache
* Connected to example.com (203.0.113.10) port 443
< HTTP/2 200

If the "Connected to" line shows your new IP, the request went to the new server. If curl never prints "Added", the syntax is off (usually the wrong port), and it fell back to normal DNS.

Testing a new server, step by step

1. Check the response code and server

NEW=203.0.113.10
curl -sI --resolve example.com:443:$NEW https://example.com/ | head -5

Look for a 200 or the redirect you expect, and a Server header or custom header that proves it's the new box.

2. Cover www and redirects

--resolve only applies to the exact host and port you give it. If the site redirects to www, map both, or curl will follow the redirect to the old server via real DNS:

curl -sIL \
  --resolve example.com:443:$NEW \
  --resolve www.example.com:443:$NEW \
  --resolve example.com:80:$NEW \
  https://example.com/ | grep -iE "^HTTP|^location"

3. Check several pages at once

for p in / /about/ /contact/ /wp-login.php /sitemap.xml; do
  curl -s -o /dev/null -w "%{http_code}  %{time_total}s  $p\n" \
    --resolve example.com:443:$NEW "https://example.com$p"
done

4. Compare old and new

diff <(curl -s https://example.com/) \
     <(curl -s --resolve example.com:443:$NEW https://example.com/)

Dynamic bits like nonces and timestamps will differ. Missing sections, error messages or a different <title> will stand out.

5. Confirm the TLS certificate

curl -sv -o /dev/null --resolve example.com:443:$NEW https://example.com/ 2>&1 \
  | grep -E "subject:|expire date:|issuer:"

Because SNI is sent correctly, this shows exactly the certificate visitors will get after the switch.

--connect-to: when you need a different port or a hostname

--connect-to HOST1:PORT1:HOST2:PORT2 redirects the connection for HOST1:PORT1 to HOST2:PORT2. It's more flexible than --resolve:

# New server listens on 8443 during testing
curl --connect-to example.com:443:203.0.113.10:8443 https://example.com/

# Point at a load balancer or another hostname instead of an IP
curl --connect-to example.com:443:lb-eu1.myhost.net:443 https://example.com/

# Empty fields mean "any" (left side) or "unchanged" (right side):
# send example.com on any port to the new IP, keeping the port
curl --connect-to example.com::203.0.113.10: https://example.com/

As with --resolve, the URL, Host header and SNI all stay example.com. Use --resolve when you just have an IP, and --connect-to when the port differs or the target is a hostname.

Why not just use -H "Host:"?

curl -H "Host: example.com" https://203.0.113.10/   # don't do this for HTTPS

Over plain HTTP this works. Over HTTPS it breaks in two ways. curl sends no SNI for an IP address, so a server hosting several sites may hand back the wrong certificate. curl also checks the certificate against 203.0.113.10, not the domain, so you get a name mismatch error. Adding -k hides the error but also hides real certificate problems. --resolve avoids all of that.

Common errors and what they mean

ErrorLikely causeFix
(7) Failed to connect ... Connection refusedNothing listening on that port, or a firewall rejecting itss -tlnp on the server; open 80/443 in the firewall
(28) Connection timed outFirewall dropping packets, wrong IPCheck the cloud provider's security group and the IP
(60) SSL: no alternative certificate subject name matchesThe new server has no certificate for this domain yet (often a default or self-signed one)Install the certificate, or add -k for a temporary check only
(60) unable to get local issuer certificateIncomplete chain: intermediate certificate missingServe the full chain (fullchain.pem), not just the leaf
Response comes from the old serverPort in --resolve doesn't match the URL, or a redirect went to an unmapped hostUse -v and check for "Added ... to DNS cache"; map www too
404 or the wrong siteWeb server vhost not configured for this domainAdd the domain to server_name / ServerName

When curl isn't enough

curl is ideal for headers, status codes, certificates and scripted checks. It won't run JavaScript, render the layout, or let a client click around. For that you need a browser pointed at the new IP. You can edit the hosts file on every device involved, or open skipdns.dev, enter the new IP and domain, and get a link that loads the site from the new server under its real hostname in any browser. It's free with no account, and links last 7 days. This comparison of preview tools covers when each one fits.

Once the checks pass, the DNS switch is routine. The website migration checklist covers the order of steps, and DNS propagation time explains how long the switch takes to reach everyone.

FAQ

What does curl --resolve do?

It adds a temporary entry to curl's DNS cache for one command: --resolve example.com:443:203.0.113.10 makes curl connect to 203.0.113.10 whenever the URL uses example.com on port 443. The Host header and TLS SNI still say example.com.

What's the difference between --resolve and --connect-to?

--resolve maps a host and port to an IP address. --connect-to maps a host and port to another host and port, so it can also change the port or point at a hostname such as a load balancer. Both keep the original Host header and SNI.

Why not just set the Host header with -H?

It works for plain HTTP, but over HTTPS curl sends the IP (no SNI) in the TLS handshake and checks the certificate against the IP, so you get the wrong certificate or a name mismatch error. --resolve gets the Host header, SNI and certificate check right.

Does --resolve work with redirects?

Only for the host and port you mapped. If the site redirects from example.com to www.example.com, add a second --resolve for www.example.com, or curl will follow the redirect using real DNS.

How do I use --resolve with IPv6?

Put the address in square brackets: --resolve example.com:443:[2001:db8::10].

← Preview a site before DNS changes with skipdns.dev