When you’re debugging load balancers, CDNs, or virtual hosts, you’ll often need to hit a specific IP address but still send the Host header for the site you care about. Here’s the right way to do it for both HTTP and HTTPS—without breaking TLS.
TL;DR
- HTTP (no TLS):
12curl -H 'Host: example.com' http://203.0.113.10/path - HTTPS (TLS/SNI & cert validation intact) – preferred:
12curl --resolve example.com:443:203.0.113.10 https://example.com/path - HTTPS alternative (endpoint rewrite, keeps SNI intact):
12curl --connect-to example.com:443:203.0.113.10:443 https://example.com/path
⚠️ Don’t do
https://203.0.113.10/...with only-H 'Host: example.com'. TLS will fail because SNI/cert validation will be against the IP, not the hostname.
Why this matters
- Name-based virtual hosting: Many sites share the same IP. The server uses the Host header (HTTP/1.1) or
:authority(HTTP/2) to route your request. - TLS/SNI: For HTTPS, the client must present the hostname during the handshake (SNI) so the server can serve the right certificate. If you connect to an IP directly, SNI uses the IP and cert validation fails.
HTTP (no TLS): just set Host
If there’s no TLS involved (port 80 or internal plain HTTP), put the IP in the URL and override the Host header.
|
1 2 |
curl -H 'Host: example.com' http://203.0.113.10/ |
IPv6:
|
1 2 |
curl -H 'Host: example.com' http://[2001:db8::10]/ |
HTTPS (with TLS): use --resolve (recommended)
--resolve injects a one-off DNS entry for this curl invocation. Curl will:
- Connect to the IP you specify
- Use
example.comfor SNI - Validate the certificate against
example.com - Send
Host: example.com
|
1 2 |
curl --resolve example.com:443:203.0.113.10 https://example.com/ |
Custom port:
|
1 2 |
curl --resolve example.com:8443:203.0.113.10 https://example.com:8443/health |
IPv6:
|
1 2 |
curl --resolve example.com:443:[2001:db8::10] https://example.com/ |
You can repeat --resolve multiple times to add several entries (e.g., www.example.com, api.example.com, etc.).
HTTPS alternative: --connect-to
--connect-to rewrites the network endpoint while leaving the URL host intact. That means:
- SNI and certificate checks still use the URL hostname
- TCP connection goes to the IP/port you specify
|
1 2 |
curl --connect-to example.com:443:203.0.113.10:443 https://example.com/api/status |
This is handy when you want to redirect only certain host:port pairs or when testing via proxies where --resolve might not apply the way you expect.
Choosing between --resolve and --connect-to
| Use case | --resolve |
--connect-to |
|---|---|---|
Act like a temporary /etc/hosts entry |
✅ Best | ➖ Not the goal |
| Keep SNI & cert validation on original hostname | ✅ | ✅ |
| Works nicely for many hostnames (repeat flag) | ✅ | ✅ (with multiple rules) |
| Proxy scenarios where DNS is handled elsewhere | ➖ Sometimes | ✅ Often simpler |
| Intention: “pretend DNS says X for this host:port” | ✅ | ➖ |
In most simple HTTPS tests,
--resolveis the most natural/explicit choice.
Quick recipes
Send a custom header and see response headers (HTTP):
|
1 2 |
curl -i -H 'Host: example.com' http://203.0.113.10/ |
Check just the status and headers (HTTPS):
|
1 2 |
curl -I --resolve example.com:443:203.0.113.10 https://example.com/ |
Verbose TLS debug:
|
1 2 |
curl -v --resolve example.com:443:203.0.113.10 https://example.com/ |
Custom CA (staging PKI):
|
1 2 3 4 |
curl --resolve example.com:443:203.0.113.10 \ --cacert /path/to/ca.pem \ https://example.com/ |
(If you must) bypass TLS verification for a one-off test
Not recommended outside of controlled debugging.
|
1 2 |
curl -k --connect-to example.com:443:203.0.113.10:443 https://example.com/ |
Common pitfalls
- HTTPS to an IP +
Hostheader: Fails SNI/cert validation because the TLS handshake is for the IP, not the hostname. - Forgetting the port in
--resolve: The mapping key ishost:port. If the site listens on8443, include it. - Wrong certificate: Even with
--resolve, the server must present a certificate valid for the hostname you’re using (e.g.,example.comor a matching SAN). - IPv6 syntax: Wrap IPv6 literals in brackets in URLs; in
--resolve, put the literal in brackets as shown above.
Troubleshooting checklist
- Use verbose output
-vshows which address curl connects to and the SNI/cert details.
12curl -v --resolve example.com:443:203.0.113.10 https://example.com/ - Confirm the virtual host
Add-H 'Host: …'for plain HTTP; rely on the URL host for HTTPS with--resolve/--connect-to. - Check ports
Is the service on 443 or a nonstandard port? Mirror that in both the URL and the flag. - Validate cert path
If you’re using a private CA, provide--cacertor--capathinstead of-k.
Copy-paste examples (replace with your values)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# HTTP curl -H 'Host: yourdomain.com' http://203.0.113.10/health # HTTPS using --resolve curl --resolve yourdomain.com:443:203.0.113.10 https://yourdomain.com/health # HTTPS with nonstandard port curl --resolve yourdomain.com:8443:203.0.113.10 https://yourdomain.com:8443/health # HTTPS using --connect-to curl --connect-to yourdomain.com:443:203.0.113.10:443 https://yourdomain.com/health # IPv6 (HTTPS) curl --resolve yourdomain.com:443:[2001:db8::10] https://yourdomain.com/ |




