Skip to content
Transport security Reviewed 2026-09-12

SSL and TLS verification settings

Some APIs still use “SSL” in option names even when the connection uses modern TLS. Turning verification off can make an encrypted connection vulnerable to server impersonation. Check both the certificate's trust chain and its match to the intended hostname; enabling one while disabling the other is incomplete.

This page preserves the existing rule URL and provides Go, Ruby, and PHP examples. The certificate validation guide covers the trust boundary and C#, Node.js, and Python clients.

Go: do not use InsecureSkipVerify for ordinary HTTPS

// Unsafe transport configuration.
transport := &http.Transport{
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
// Safer transport configuration.
transport := &http.Transport{
    TLSClientConfig: &tls.Config{
        MinVersion: tls.VersionTLS12,
        // InsecureSkipVerify remains false; RootCAs nil uses the system roots.
    },
}
client := &http.Client{Transport: transport, Timeout: 10 * time.Second}

These fragments belong inside a Go function with crypto/tls, net/http, and time imported. The minimum TLS version is a separate protocol policy. For a private CA, configure an approved root pool; do not disable checks. The Go tls.Config documentation explains that InsecureSkipVerify skips normal chain and hostname verification.

Ruby 3.4+: verify the peer and hostname

# Unsafe on a Net::HTTP instance configured for HTTPS.
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.verify_hostname = false
require 'net/http'
require 'openssl'

uri = URI('https://api.example.com/status')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.verify_hostname = true
http.open_timeout = 5
http.read_timeout = 10

The fragment uses the Ruby 3.4 Net::HTTP API and configures the client without making a request. An approved CA file/store can be configured when the service uses private PKI. Keep hostname verification enabled as well as peer verification. Older Net::HTTP versions may expose different configuration methods; upgrade unsupported runtimes and verify their actual handshake behavior. See the Ruby Net::HTTP documentation.

PHP cURL: enable both checks

// Unsafe on an existing HTTPS cURL handle.
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
// Safer; the handle targets a fixed HTTPS service URL.
$ch = curl_init('https://api.example.com/status');
if ($ch === false || !curl_setopt_array($ch, [
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_TIMEOUT => 10,
])) {
    throw new RuntimeException('Unable to configure HTTPS client');
}

This configuration does not execute the request. Check curl_exec failures in the calling code and keep the failure path closed. CURLOPT_SSL_VERIFYHOST uses 2 for hostname verification, not a boolean-style replacement. See the primary libcurl references for peer verification and hostname verification.

Regression test

In a local fixture, configure a test CA and certificates for a local test hostname. Confirm a valid chain and matching hostname succeed, while an untrusted certificate and a wrong hostname fail. Cover the deployed client/runtime configuration and verify there is no fallback that disables checks after an error. Do not run this test against unrelated external servers.

Related: certificate validation, cleartext protocols, and mobile transport configuration.

References: CWE-295 — improper certificate validation and CWE-297 — improper validation of certificate host mismatch.