Skip to content

curl --resolve and SNI: Testing Virtual Hosts Without /etc/hosts

When you need to test a virtual host on a specific IP but don’t want to edit /etc/hosts — whether due to permissions, conflicts with other services, or just the habit of keeping the file clean — curl --resolve solves both problems at once: it overrides DNS resolution and sends the correct SNI in the TLS handshake.

Problem: virtual host without editing /etc/hosts

Multiple virtual hosts can live on a single IP, and the server picks the right one based on the Host header (HTTP/1.1) and SNI (TLS). Without an entry in /etc/hosts, curl first tries to resolve the name through DNS — getting the wrong IP, or no response at all.

Editing /etc/hosts works, but it requires sudo, clutters the file, and can break other services that depend on the same record.

curl –resolve: syntax and example

The --resolve flag intercepts name resolution at the curl level and substitutes it:

curl --resolve HOST:PORT:ADDR URL
ComponentMeaning
HOSTVirtual host name
PORTPort (typically 443 for HTTPS)
ADDRTarget IP address

Example:

curl --resolve example.com:443:203.0.113.50 https://example.com/health

curl sends the request to 203.0.113.50:443, but the HTTP Host header will still be example.com.

SNI in combination with –resolve

Note

--resolve does not change the name curl sends in SNI. SNI is taken from the URL.

This is the key point. If the URL contains https://example.com, curl sends example.com as SNI in the TLS ClientHello — even though the IP is overridden via --resolve. The server uses SNI to select the correct certificate and virtual host.

To verify that SNI is actually being sent, use openssl:

openssl s_client -connect 203.0.113.50:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer

If SNI is empty or wrong, the server returns the default certificate — and curl produces an error like SSL: certificate subject name does not match.

Practical example: checking a virtual host

Suppose several virtual hosts run on server 198.51.100.10, and you need to check app.local without touching /etc/hosts:

curl -v --resolve app.local:443:198.51.100.10 https://app.local/api/status

In the -v output you will see:

  • Connected to 198.51.100.10 (198.51.100.10) port 443 — connection to the correct IP.
  • > Host: app.local — correct Host header.
  • TLS SNI extension: "app.local" — SNI sent correctly.

For bulk-checking multiple hosts on the same IP:

for host in app.local api.local admin.local; do
  echo "=== $host ==="
  curl --resolve "$host:443:198.51.100.10" "https://$host/health" -s -o /dev/null -w "%{http_code}\n"
done
Warning

--resolve applies only to the current curl process. Once the command finishes, the override disappears — /etc/hosts stays untouched.

If you need the override to work for all tools in the terminal, not just curl, then nsupdate or a local DNS resolver (dnsmasq, stubby) is a better fit. But for a one-off virtual host check, --resolve is faster and safer.