How to Test HTTPS URL with Specific IP Address While Preserving SSL Certificate Validation


2 views

When testing load-balanced HTTPS services, developers often need to target specific backend servers while maintaining proper SSL certificate validation. The standard approach of using curl https://DOMAIN.TLD doesn't let us specify which backend IP to test, while using curl https://IP_ADDRESS breaks certificate validation since the cert is issued for the domain, not the IP.

# Problematic approach:
curl https://192.0.2.1 -H 'Host: example.com'
# Results in SSL certificate error: hostname doesn't match

Modern versions of cURL (7.21.3+) support Server Name Indication (SNI) through the --resolve flag, which solves this elegantly:

# Correct way to test specific IP with SSL validation:
curl https://example.com --resolve example.com:443:192.0.2.1

This command does three things:
1. Forces resolution of example.com to 192.0.2.1 for port 443
2. Maintains proper Host header
3. Preserves full SSL certificate validation

For older systems or special cases, consider these approaches:

Using --connect-to (cURL 7.49+)

curl --connect-to example.com:443:192.0.2.1:443 https://example.com

Modifying /etc/hosts Temporarily

# Add to /etc/hosts
192.0.2.1 example.com

# Then run normal curl
curl https://example.com

# Remember to remove the entry after testing!

For low-level debugging, openssl can verify the SSL handshake directly:

openssl s_client -connect 192.0.2.1:443 -servername example.com -showcerts

While testing with -k/--insecure might seem tempting, it defeats the purpose of HTTPS validation. Production testing should always maintain certificate validation to catch potential MITM attacks or misconfigurations.

For automated testing, consider setting up proper DNS records or using service discovery instead of hardcoding IPs.


When testing load-balanced HTTPS services, developers often need to target specific backend servers by IP while maintaining proper SSL validation. The fundamental issue arises because:

curl https://192.0.2.1 -H 'Host: example.com'

Triggers a certificate validation failure since the SSL certificate is issued for example.com but we're connecting to an IP address.

Modern HTTPS implementations enforce strict hostname verification through two mechanisms:

  1. Server Name Indication (SNI) in the TLS handshake
  2. Certificate Subject Alternative Name (SAN) validation

When you specify an IP address in the URL, curl automatically uses that for both the TCP connection and certificate validation.

cURL provides two powerful options for this scenario:

curl https://example.com \
  --resolve 'example.com:443:192.0.2.1' \
  --connect-timeout 3

This approach:

  • Maintains proper HTTPS validation by using the domain in the URL
  • Forces the connection to your specified IP via DNS override
  • Keeps all SSL certificate checks intact

For more complex cases, you might need:

Multiple Backend Testing

#!/bin/bash
servers=("192.0.2.1" "192.0.2.2" "192.0.2.3")
for ip in "${servers[@]}"; do
  echo "Testing $ip"
  curl -sS https://example.com \
    --resolve "example.com:443:$ip" \
    -H "X-Test: backend-check" \
    -o /dev/null \
    -w "HTTP %{http_code} - Size: %{size_download} bytes\n"
done

Certificate Inspection

To verify certificates while targeting specific IPs:

openssl s_client -connect 192.0.2.1:443 \
  -servername example.com \
  -showcerts < /dev/null 2>/dev/null | \
  openssl x509 -noout -text | \
  grep -A1 "Subject Alternative Name"

When working with tools that don't support cURL's advanced features:

Hosts File Override

# Temporary DNS override (Linux/macOS)
sudo sh -c "echo '192.0.2.1 example.com' >> /etc/hosts"
curl https://example.com
sudo sed -i '/192.0.2.1 example.com/d' /etc/hosts

Using HTTP/1.1 Host Header

For non-SNI compatible servers (not recommended for production):

curl https://192.0.2.1 \
  --http1.1 \
  -H "Host: example.com" \
  --cacert /path/to/cert.pem
  • Always verify the IP actually hosts the service: nc -zv 192.0.2.1 443
  • Check for protocol mismatches with --tlsv1.2 or --tlsv1.3
  • Use --verbose to see complete handshake details