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

LLMS index: [llms.txt](/en/llms.txt)

---

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
```

| Component | Meaning |
|---|---|
| `HOST` | Virtual host name |
| `PORT` | Port (typically `443` for HTTPS) |
| `ADDR` | Target IP address |

Example:

```bash
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`:

```bash
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`:

```bash
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:

```bash
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.
