Back to blog
Security & TLS September 15, 2026

SSL/TLS Certificate for IP Address vs FQDN: RFC 5280 Architecture & SRE Implementation Guide

Automate WhatsApp Alerts
Start Free ➔

An SRE deploys an internal API gateway, Kubernetes ingress controller, or edge load balancer. The service is secured with a valid, publicly trusted TLS certificate issued for api.example.com.

However, when an automated telemetry collector, container sidecar, or monitoring probe attempts to connect directly to the service's IP address (https://198.51.100.10:443), the TLS handshake fails abruptly with:

curl: (60) SSL: no alternative certificate subject name matches target host name '198.51.100.10'

or in Go / Kubernetes runtimes:

x509: certificate is valid for api.example.com, not 198.51.100.10

Why does this happen even when DNS resolves api.example.com directly to 198.51.100.10?

The answer lies in the fundamental distinction between IP routing and X.509 cryptographic identity verification established in RFC 5280 and RFC 6125. In modern TLS clients, certificate validation does not verify where network packets are physically routed—it strictly verifies the identifier parsed from the target URI against the Subject Alternative Name (SAN) extension in the X.509 certificate.

In this deep-dive guide, we dissect the RFC 5280 ASN.1 certificate identity model, compare dNSName vs iPAddress SAN encodings, demystify TLS SNI vs HTTP Host headers, generate compliant OpenSSL IP certificates, and establish automated multi-SAN certificate lifecycle management.


1. RFC 5280 Certificate Identity Model & ASN.1 Semantics

In the X.509 Public Key Infrastructure standard (RFC 5280), every digital certificate contains a TBSCertificate (To-Be-Signed) structure. Historically, the Common Name (CN) field in the Subject DN was used for hostname verification.

The Common Name is formally deprecated. Modern TLS implementations (Go crypto/tls, OpenSSL 1.1.1+, BoringSSL, Rustls, Node.js, and web browsers) enforce RFC 6125 and strictly require the server identity to appear in the SubjectAltName (SAN) extension.

                  RFC 5280 X.509 CERTIFICATE STRUCTURE

   ┌─────────────────────────────────────────────────────────────┐
   │ TBSCertificate (To-Be-Signed)                               │
   │  ├── Version: v3 (0x2)                                      │
   │  ├── Serial Number                                          │
   │  ├── Signature Algorithm: ecdsa-with-SHA256                 │
   │  ├── Issuer: CN=Let's Encrypt Authority                     │
   │  ├── Validity: Not Before / Not After                       │
   │  ├── Subject: CN=api.example.com (DEPRECATED FOR IDENTITY!) │
   │  ├── SubjectPublicKeyInfo                                   │
   │  └── Extensions (X509v3 Extensions)                         │
   │        │                                                    │
   │        ▼                                                    │
   │     SubjectAlternativeName (SAN) ::= GeneralNames           │
   │        ├── [2] dNSName: "api.example.com"                   │
   │        └── [7] iPAddress: 0xC633640A (198.51.100.10)       │
   └─────────────────────────────────────────────────────────────┘

ASN.1 GeneralName Specification (RFC 5280 § 4.2.1.6)

The SubjectAltName extension is defined in ASN.1 as a sequence of GeneralName choice elements:

GeneralName ::= CHOICE {
    otherName                       [0] OtherName,
    rfc822Name                      [1] IA5String,
    dNSName                         [2] IA5String,
    x400Address                     [3] ORAddress,
    directoryName                   [4] Name,
    ediPartyName                    [5] EDIPartyName,
    uniformResourceIdentifier       [6] IA5String,
    iPAddress                       [7] OCTET STRING,
    registeredID                    [8] OBJECT IDENTIFIER
}

Why dNSName and iPAddress Cannot Be Interchanged

Notice the critical structural difference:

  • dNSName (Tag [2]): Encoded as an IA5String (ASCII string representing an FQDN, e.g. "api.example.com").
  • iPAddress (Tag [7]): Encoded as an OCTET STRING (raw binary bytes):
    • For IPv4: Exactly 4 bytes (0xC6 0x33 0x64 0x0A for 198.51.100.10).
    • For IPv6: Exactly 16 bytes (0x20 0x01 0x0D 0xB8 ... for 2001:db8::10).

⚠️ THE ASCII IP MISTAKE: If you generate an OpenSSL config with DNS.1 = 198.51.100.10, OpenSSL encodes the IP string as a dNSName (Tag [2]). When a client connects to https://198.51.100.10, the TLS verifier treats the target as an IP address and searches strictly for Tag [7] (iPAddress), completely ignoring the dNSName entry and throwing a validation error!


2. Technical Comparison: FQDN SAN vs IP Address SAN

Identifier TypeCorrect SAN EntryValid for https://api.example.comValid for https://198.51.100.10Wildcard SupportPublic CA Availability
Fully Qualified Domain Name (FQDN)DNS:api.example.comValid❌ Fails validation✅ Yes (*.example.com)Free & Instant (Let's Encrypt, ZeroSSL, AWS ACM)
Public IPv4 AddressIP:198.51.100.10❌ Fails validationValidNo (RFC 6125 forbids IP wildcards)Paid Commercial CAs (DigiCert, Sectigo, GlobalSign) or ZeroSSL
Public IPv6 AddressIP:2001:db8::10❌ Fails validationValidNoCommercial CAs / ZeroSSL
Private RFC 1918 / Loopback IPIP:10.0.4.12 or IP:127.0.0.1❌ Fails validationValid (Internal PKI only)NoForbidden by CA/Browser Forum Baseline Requirements
Dual Identity (Hybrid SAN)DNS:api.example.com + IP:198.51.100.10ValidValid✅ (On DNS only)Supported by Commercial CAs & Private Internal CAs

3. The 4-Layer Connection Architecture: Network vs TLS vs HTTP

To diagnose TLS handshake failures, you must separate the four distinct layers involved in establishing an encrypted session:

                    THE 4-LAYER CLIENT REQUEST STACK

   Layer 1: DNS Resolution Plane
   User requests "api.example.com" ──► DNS resolves to 198.51.100.10
                  │
                  ▼
   Layer 2: Network / TCP Transport Layer
   Client initiates TCP 3-way handshake to 198.51.100.10:443 (Destination IP)
                  │
                  ▼
   Layer 3: TLS Cryptographic Session Layer
   - Client sends TLS ClientHello with SNI = "api.example.com"
   - Server returns Certificate containing SAN = "DNS:api.example.com"
   - Client matches URI Hostname ("api.example.com") against SAN ──► VALIDATED!
                  │
                  ▼
   Layer 4: Application / HTTP Protocol Layer
   Encrypted HTTP/1.1 or HTTP/2 request:
   Host: api.example.com (or :authority in HTTP/2)

Diagnostic Comparison: curl Connection Modes

Observe how the client behavior changes across different invocation patterns:

# Mode 1: Standard FQDN Connection (DNS + SNI + FQDN SAN)
curl -v https://api.example.com/
# -> SNI: api.example.com | Host: api.example.com | Verifies DNS:api.example.com

# Mode 2: Direct IP Connection (NO DNS + NO SNI by default + Requires IP SAN)
curl -v https://198.51.100.10/
# -> SNI: (empty or 198.51.100.10) | Host: 198.51.100.10 | Verifies IP:198.51.100.10

# Mode 3: SRE Debugging via --resolve (Forces IP without breaking TLS!)
curl -v --resolve api.example.com:443:198.51.100.10 https://api.example.com/
# -> Routes packets to 198.51.100.10, BUT sends SNI: api.example.com and verifies DNS:api.example.com!

💡 THE SRE --resolve SUPERPOWER: During DNS outages or when validating a single backend server behind an Anycast CDN, never connect via raw IP. Always use curl --resolve <FQDN>:443:<IP> https://<FQDN>/ to preserve SNI and hostname certificate validation while forcing the network route to your target IP.


4. Inspecting Certificate SANs with OpenSSL

When troubleshooting certificate errors, inspect the remote endpoint's live X.509 extensions:

# 1. Connect with SNI and extract SAN extensions
openssl s_client -connect 198.51.100.10:443 -servername api.example.com -showcerts </dev/null 2>/dev/null | \
  openssl x509 -noout -text | grep -A 3 "Subject Alternative Name"

Expected Output for Compliant Certificates

Scenario A: Standard FQDN Certificate

X509v3 Subject Alternative Name: 
    DNS:api.example.com, DNS:www.example.com

Scenario B: Compliant IP Address Certificate

X509v3 Subject Alternative Name: 
    IP Address:198.51.100.10, IP Address:2001:db8::10

Scenario C: Dual FQDN + IP Certificate

X509v3 Subject Alternative Name: 
    DNS:api.example.com, IP Address:198.51.100.10

5. Generating an RFC 5280-Compliant IP Certificate with OpenSSL

If you are issuing certificates for internal services, appliances, or bare-metal Kubernetes nodes via Private PKI or HashiCorp Vault, use this OpenSSL configuration.

Step 1: Create openssl-ip.cnf

[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
C = US
ST = California
L = San Francisco
O = Pingzoapp Infrastructure
CN = 198.51.100.10

[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names

[alt_names]
# DNS Hostnames
DNS.1 = api.example.com
DNS.2 = api-internal.example.net

# IPv4 Addresses (Encoded as OCTET STRING under RFC 5280 Tag [7])
IP.1 = 198.51.100.10
IP.2 = 10.0.4.12
IP.3 = 127.0.0.1

# IPv6 Addresses
IP.4 = 2001:0db8:85a3:0000:0000:8a2e:0370:7334

Step 2: Generate Private Key and CSR

openssl req -new -nodes -newkey rsa:2048 \
  -keyout server.key \
  -out server.csr \
  -config openssl-ip.cnf

Step 3: Self-Sign or Sign with Private CA

openssl x509 -req -days 365 \
  -in server.csr \
  -signkey server.key \
  -out server.crt \
  -extensions v3_req \
  -extfile openssl-ip.cnf

Step 4: Verify the ASN.1 Binary SAN Structure

openssl x509 -in server.crt -noout -text | grep -A 4 "Subject Alternative Name"

6. Kubernetes, cert-manager & Service Mesh Implementation

In Kubernetes environments using cert-manager, you must explicitly populate the ipAddresses array alongside dnsNames:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: internal-gateway-tls
  namespace: istio-system
spec:
  secretName: internal-gateway-tls-secret
  duration: 2160h # 90 days
  renewBefore: 360h # 15 days
  subject:
    organizations:
      - Pingzoapp
  # FQDN Identifiers (dNSName Tag [2])
  dnsNames:
    - gateway.internal.local
    - istio-ingress.internal.local
  # IP Identifiers (iPAddress Tag [7])
  ipAddresses:
    - 10.96.0.1          # Kubernetes Service ClusterIP
    - 192.168.1.100      # Node / MetalLB Ingress IP
    - 127.0.0.1
  issuerRef:
    name: vault-cluster-issuer
    kind: ClusterIssuer

7. Public CA vs Private PKI Trust Boundaries

Why can't you get a free Let's Encrypt certificate for your internal IP 10.0.4.12 or public elastic IP?

                    CA/BROWSER FORUM TRUST BOUNDARY

  Public Trust Store (Mozilla / Apple / Google / Microsoft)
  ┌─────────────────────────────────────────────────────────────┐
  │ Public Certificate Authorities (Let's Encrypt, DigiCert)    │
  │                                                             │
  │  Rule 1: MUST validate domain control via ACME / DNS-01     │
  │  Rule 2: FORBIDDEN from issuing certs for RFC 1918 IPs      │
  │          (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, .local)│
  │  Rule 3: Public IP certs require Organization Validation    │
  │          and BGP / WHOIS IP block ownership verification.   │
  └─────────────────────────────────────────────────────────────┘

When to Use Which Identity

Architecture ModelPrimary SAN TypeRecommended Use CasesManagement Overhead
Model A: Pure FQDNDNS:api.example.comPublic web apps, SaaS APIs, microservices with service discovery.Lowest: IP changes require zero certificate rotation.
Model B: Pure IP SANIP:198.51.100.10Legacy appliances, IoT devices, hardcoded IP firmware, air-gapped SCADA.High: Changing or reassigning IP invalidates certificate immediately.
Model C: Hybrid FQDN + IPDNS:... + IP:...Kubernetes Ingress, Envoy proxies, internal zero-trust mTLS gateways.Medium: Requires synchronized rotation when cluster node IPs change.

8. SRE Troubleshooting & Diagnostic Runbook

Follow this 5-step checklist when encountering certificate verification failures:

                  TLS IDENTITY DECISION WORKFLOW

  Client encounters TLS Handshake Failure
                │
                ▼
  1. Inspect URI Scheme & Target:
     Is the client connecting via FQDN (https://domain) or raw IP (https://1.2.3.4)?
                │
         ┌──────┴────────────────────────┐
         ▼                               ▼
    Target is FQDN                  Target is RAW IP
         │                               │
  2. Query DNS A/AAAA             2. Inspect Remote SAN via openssl:
     Does FQDN point to IP?          Does SAN contain "IP Address:1.2.3.4"?
         │                               │
         ├── NO ──► Fix DNS Record       ├── NO ──► Reissue Cert with IP SAN
         └── YES                         └── YES
              │                               │
  3. Inspect Remote SAN:          3. Is the IP Private (10.x / 192.168.x)?
     Does SAN have "DNS:domain"?         │
         │                               ├── YES ──► Distribute Private Root CA
         ├── NO ──► Reissue Cert         └── NO  ──► Verify CA Chain & Expiry
         └── YES ──► Check SNI header

Common SRE Diagnostic Commands

# 1. Check if server presents different certificates depending on SNI
echo "--- Testing with SNI ---"
openssl s_client -connect 198.51.100.10:443 -servername api.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer

echo "--- Testing without SNI (Default Fallback Cert) ---"
openssl s_client -connect 198.51.100.10:443 </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer

# 2. Check remaining validity days on live endpoint
echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null | \
  openssl x509 -noout -dates

9. SRE Production Checklist for TLS Certificates

  • SAN Invariance: All client-facing identities (both DNS hostnames and static IPs) are declared in the SubjectAlternativeName extension.
  • Tag Precision: Ensure IP addresses are defined under RFC 5280 Tag [7] (IP Address:), not Tag [2] (DNS:).
  • SNI Alignment: Reverse proxies (NGINX, Envoy, HAProxy) have default fallback certificates configured for direct-IP probes lacking SNI.
  • Trust Store Distribution: For internal IP certificates signed by a private CA, verify that the root CA certificate is installed in /etc/ssl/certs and language trust stores (Java keystore, Node.js NODE_EXTRA_CA_CERTS).
  • Automated Expiry Alerts: Alerts fire at 30 days and 7 days prior to certificate expiration.

Conclusion & Next Steps

In the modern TLS ecosystem, the golden rule of X.509 certificate validation is simple:

The certificate identity must match the exact identifier the client verifies in its URI, not merely the IP address where packets are routed.

By understanding the ASN.1 structure of dNSName vs iPAddress SANs, leveraging curl --resolve for routing-isolated validation, and managing certificate identities through automated declarative pipelines, you can eliminate certificate identity mismatches across your production fleet.


Inspect and Validate TLS Certificates with Pingzoapp

Avoid unexpected TLS outages, certificate expiration surprises, and SAN mismatch errors.

With Pingzoapp, you get:

  • Comprehensive SSL/TLS Monitoring: Multi-region certificate inspection alerting you on expiration, invalid chains, and weak cipher suites.
  • Interactive SSL Inspector: Analyze live TLS certificates, SAN entries, and trust chains instantly using our free SSL Inspector.
  • DNS & Network Diagnostics: Audit A/AAAA records and nameserver consistency with the DNS Lookup Tool.
  • Instant Multi-Channel Alerts: Get notified via WhatsApp, Telegram, SMS, Slack, and Discord weeks before certificates expire.

👉 Start Monitoring SSL Certificates Free with Pingzoapp and protect your endpoints against TLS downtime.

Zero-Code Uptime Alerts

Stop Finding Out About Outages from Angry Users

Get instant WhatsApp & Discord alerts the second your API, website, or server goes down. Setup in 30 seconds with 60-second checks.

WhatsApp & Discord 60-Second Checks Free Forever Plan
Try Pingzo Free

Know before your users do

Connect official WhatsApp notification channels, Discord webhooks, Telegram bots, and public status pages. Start in 30 seconds.

Create Free Monitor