SSL/TLS Certificate Installation & Validation: Complete Implementation Guide
SSL/TLS certificates are the foundation of secure internet communication, yet certificate installation remains a critical pain point for organizations. Improper installation leads to expensive outages, security vulnerabilities, and compliance violations. This comprehensive guide covers the complete installation and validation workflow, from web server configuration through advanced security testing, enabling you to deploy certificates with confidence.
Table of Contents
- Introduction
- Web Server Installation
- Load Balancer & CDN Configuration
- Certificate Chain Verification
- Multi-Server Deployment Automation
- Certificate Content Analysis
- Certificate Transparency Verification
- SSL/TLS Protocol Testing
- Security Headers Validation
- Mixed Content Detection & Remediation
- Common Issues & Troubleshooting
- Validation Checklist
Introduction
Why Certificate Installation Matters
Installing SSL/TLS certificates correctly is not merely a technical checkbox—it directly impacts security posture, user trust, and business continuity. Studies show that 42% of Infrastructure-as-Code templates contain certificate misconfigurations, and misconfigured certificates are among the top causes of unexpected service outages.
The Real-World Impact:
- Certificate misconfiguration downtime costs average $5,600 per minute
- Browser warnings erode customer trust and increase bounce rates by 70%
- Incomplete certificate chains cause 15-20% of SSL/TLS failures
- Weak cipher suites expose organizations to downgrade attacks and credential theft
What This Guide Covers
This guide focuses on Stage 3 and Stage 4 of the certificate lifecycle (from the comprehensive SSL/TLS Certificate Lifecycle Management workflow):
- Stage 3: Certificate Installation & Deployment - Deploy certificates securely across your infrastructure
- Stage 4: Certificate Validation & Security Testing - Verify proper installation and eliminate security gaps
Related Overview
For the complete certificate lifecycle context, see SSL/TLS Certificate Lifecycle Management.
Web Server Installation
Nginx Configuration
Nginx is the most performant web server for HTTPS, handling certificate installation through the ssl_certificate and ssl_certificate_key directives.
Basic Nginx HTTPS Configuration
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# Certificate files
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/private.key;
# Modern TLS configuration (2025 best practices)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Additional security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# HTTP/2 ALPN negotiation
ssl_alpn h2 http/1.1;
# Logging
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
# Application configuration
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$server_name$request_uri;
}
}
Key Configuration Points
Certificate Paths:
ssl_certificate: Full certificate chain (recommended) or just the leaf certificatessl_certificate_key: Private key file (must not be world-readable:chmod 600)- Using fullchain.pem simplifies configuration and prevents chain issues
TLS Protocol Version:
- TLSv1.2 minimum (TLS 1.0 and 1.1 deprecated)
- TLSv1.3 recommended for modern clients
- Disable SSLv3, TLS 1.0, and TLS 1.1 completely
Cipher Suite Ordering:
ssl_prefer_server_ciphers off(modern approach for TLS 1.3)- List ciphers in preference order for TLS 1.2 clients
- Include ECDHE-based ciphers for Perfect Forward Secrecy (PFS)
Performance Optimization:
ssl_session_cache: Share SSL sessions across worker processesssl_session_timeout: Balance between security and performancessl_session_tickets off: Disable tickets to prevent key disclosure vulnerabilities
OCSP Stapling:
- Server fetches OCSP response and includes it in handshake
- Improves performance (avoids client OCSP queries)
- Enhances privacy (CA doesn't know which sites users visit)
Testing Nginx Configuration
# Validate syntax before reloading
nginx -t
# Reload Nginx without dropping connections
sudo systemctl reload nginx
# Verify certificate is loaded correctly
openssl s_client -connect example.com:443 -servername example.com
Apache Configuration
Apache uses the mod_ssl module to handle HTTPS connections.
Basic Apache HTTPS Configuration
# Load mod_ssl module
LoadModule ssl_module modules/mod_ssl.so
# Virtual host for HTTPS
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
# Certificate files
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/cert.pem
SSLCertificateKeyFile /etc/apache2/ssl/private.key
SSLCertificateChainFile /etc/apache2/ssl/chain.pem
# Modern SSL/TLS configuration
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305'
SSLHonorCipherOrder off
SSLCompression off
SSLSessionCache shmcb:/var/run/apache2/ssl_cache(512000)
SSLSessionCacheTimeout 300
SSLSessionTickets off
# HSTS Header
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
# Security headers
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "no-referrer-when-downgrade"
# OCSP Stapling
SSLUseStapling on
SSLStaplingCache shmcb:/var/run/apache2/ocsp_cache(128000)
SSLStaplingResponseMaxAge 3600
SSLStaplingFakeTryAll off
SSLStaplingResponderTimeout 5
# Logging
CustomLog /var/log/apache2/example.com.access.log combined
ErrorLog /var/log/apache2/example.com.error.log
# Application configuration
ProxyPreserveHost On
ProxyPass / http://backend_servers/
ProxyPassReverse / http://backend_servers/
</VirtualHost>
# HTTP virtual host - redirect to HTTPS
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
# Allow Let's Encrypt ACME challenges
Alias /.well-known/acme-challenge /var/www/certbot/.well-known/acme-challenge
<Directory /var/www/certbot/>
Require all granted
</Directory>
# Redirect all other traffic to HTTPS
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>
# Global OCSP stapling cache configuration
SSLStaplingCache shmcb:/var/run/apache2/ocsp(128000)
SSLStaplingResponseMaxAge 3600
Key Configuration Points
Certificate Paths:
SSLCertificateFile: Leaf certificate (just your domain)SSLCertificateKeyFile: Private key fileSSLCertificateChainFile: Intermediate certificates (required in Apache)
TLS Configuration:
- Use
-allto disable all protocols, then explicitly enable TLSv1.2 and TLSv1.3 - Modern approach gives clearer intent than enabling multiple versions
Session Caching:
- Use
shmcb(shared memory cache) for performance - Size: 512000 bytes recommended for standard deployments
- Separate cache for OCSP stapling responses
Testing Apache Configuration
# Validate syntax
sudo apache2ctl configtest
# Reload Apache
sudo systemctl reload apache2
# Verify certificate is loaded
openssl s_client -connect example.com:443 -servername example.com
Windows IIS Configuration
Internet Information Services (IIS) uses a different certificate format and configuration approach.
IIS Certificate Installation Steps
-
Convert Certificate Format:
# Convert PEM to PFX (Windows format) openssl pkcs12 -export -in cert.pem -inkey private.key -out cert.pfx -certfile chain.pem -name "example.com" -
Import Certificate in IIS:
- Open IIS Manager
- Select "Server Certificates"
- Click "Import"
- Select PFX file and provide password
- Click OK
-
Bind Certificate to Website:
- Right-click on site name → Edit Bindings
- Add HTTPS binding on port 443
- Select imported certificate
- Add SNI hostname (example.com)
- Click OK
IIS Security Configuration
<!-- applicationHost.config modifications -->
<system.webServer>
<security>
<access sslFlags="Ssl,SslNegotiateCert,Ssl128" />
</security>
<staticContent>
<mimeMap fileExtension=".weba" mimeType="audio/webp" />
</staticContent>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security" value="max-age=63072000; includeSubDomains; preload" />
<add name="X-Frame-Options" value="SAMEORIGIN" />
<add name="X-Content-Type-Options" value="nosniff" />
<add name="X-XSS-Protection" value="1; mode=block" />
</customHeaders>
</httpProtocol>
<rewrite>
<rules>
<rule name="Redirect HTTP to HTTPS" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="off" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
Load Balancer & CDN Configuration
AWS Application Load Balancer (ALB)
ALB simplifies HTTPS configuration through integration with AWS Certificate Manager.
ALB HTTPS Listener Configuration
# Create target group
aws elbv2 create-target-group \\
--name backend-targets \\
--protocol HTTP \\
--port 80 \\
--vpc-id vpc-12345678
# Create HTTPS listener
aws elbv2 create-listener \\
--load-balancer-arn arn:aws:elasticloadbalancing:region:account:loadbalancer/app/lb-name/id \\
--protocol HTTPS \\
--port 443 \\
--certificates CertificateArn=arn:aws:acm:region:account:certificate/id \\
--ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 \\
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:region:account:targetgroup/backend-targets/id
# Create HTTP listener with redirect
aws elbv2 create-listener \\
--load-balancer-arn arn:aws:elasticloadbalancing:region:account:loadbalancer/app/lb-name/id \\
--protocol HTTP \\
--port 80 \\
--default-actions Type=redirect,RedirectConfig='{Protocol=HTTPS,Port=443,StatusCode=HTTP_301}'
ALB SSL Policy Recommendations:
ELBSecurityPolicy-TLS13-1-2-2021-06(TLS 1.2/1.3 only, modern ciphers)ELBSecurityPolicy-TLS13-1-3-2021-06(TLS 1.3 only, if all clients support it)- Avoid older policies (CloudFront-compatible, etc.)
Enable Access Logging
# Configure ALB to log to S3
aws elbv2 modify-load-balancer-attributes \\
--load-balancer-arn arn:aws:elasticloadbalancing:region:account:loadbalancer/app/lb-name/id \\
--attributes Key=access_logs.s3.enabled,Value=true Key=access_logs.s3.bucket,Value=my-bucket Key=access_logs.s3.prefix,Value=alb-logs
Azure Application Gateway
Azure uses a different certificate format and configuration model.
Application Gateway Certificate Configuration
# Create certificate from PFX file
az network application-gateway ssl-cert create \\
--resource-group myResourceGroup \\
--gateway-name myAppGateway \\
--name myCertificate \\
--cert-file certificate.pfx \\
--cert-password myPassword
# Create HTTPS listener
az network application-gateway http-listener create \\
--resource-group myResourceGroup \\
--gateway-name myAppGateway \\
--name myHttpsListener \\
--front-end-port-name myFrontendPort \\
--ssl-cert-name myCertificate \\
--frontend-ip appGatewayFrontendIP \\
--host-name example.com
# Create routing rule
az network application-gateway rule create \\
--resource-group myResourceGroup \\
--gateway-name myAppGateway \\
--name myRoutingRule \\
--http-listener myHttpsListener \\
--backend-pool myBackendPool \\
--http-settings myHttpSettings
Cloudflare CDN Integration
Cloudflare provides multiple certificate options depending on plan level.
Cloudflare Custom SSL Certificate Upload
# Login to Cloudflare CLI
wrangler cloudflare login
# Upload custom certificate
# Option 1: Via Cloudflare Dashboard (Business/Enterprise plans)
# - Go to SSL/TLS > Custom Certificates
# - Click "Upload Custom Certificate"
# - Paste certificate, private key, and certificate chain
# - Click Save
# Option 2: Via Cloudflare API
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_certificates" \\
-H "Authorization: Bearer {api_token}" \\
-H "Content-Type: application/json" \\
--data '{
"certificate": "-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----",
"private_key": "-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----",
"bundle_method": "ubiquitous"
}'
Cloudflare SSL/TLS Settings
# Set SSL/TLS encryption mode to Full (Strict)
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/ssl" \\
-H "Authorization: Bearer {api_token}" \\
-H "Content-Type: application/json" \\
--data '{"value": "full"}'
# Enable "Always Use HTTPS"
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/always_use_https" \\
-H "Authorization: Bearer {api_token}" \\
-H "Content-Type: application/json" \\
--data '{"value": "on"}'
# Set minimum TLS version
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/min_tls_version" \\
-H "Authorization: Bearer {api_token}" \\
-H "Content-Type: application/json" \\
--data '{"value": "1.2"}'
GCP Cloud Load Balancer
Google Cloud Platform uses its own certificate management system.
GCP SSL Certificate Configuration
# Create SSL certificate resource
gcloud compute ssl-certificates create example-com-cert \\
--certificate=cert.pem \\
--private-key=private.key \\
--global
# Create HTTPS health check
gcloud compute health-checks create https example-health-check \\
--port=443 \\
--request-path=/health
# Create backend service
gcloud compute backend-services create backend-service \\
--protocol=HTTPS \\
--health-checks=example-health-check \\
--global
# Create target HTTPS proxy
gcloud compute target-https-proxies create https-proxy \\
--ssl-certificates=example-com-cert \\
--url-map=url-map
# Create forwarding rule
gcloud compute forwarding-rules create https-rule \\
--global \\
--target-https-proxy=https-proxy \\
--address=example-ip \\
--ports=443
Certificate Chain Verification
Understanding Certificate Chains
A complete certificate chain consists of three components:
- Leaf Certificate - Your domain's certificate (example.com)
- Intermediate Certificate(s) - CA's intermediate certificate(s)
- Root Certificate - CA's root certificate (already trusted by browsers)
Browsers must be able to trace from your leaf certificate to a trusted root certificate. Missing intermediate certificates cause "untrusted certificate" errors.
Verifying Certificate Chain Completeness
Using OpenSSL Commands
# Display the complete chain from server
openssl s_client -connect example.com:443 -showcerts
# This shows:
# - Leaf certificate (Cert 1)
# - Intermediate certificate(s) (Cert 2, 3, etc.)
# - Root certificate (final cert)
# Verify certificate chain locally
openssl verify -CAfile chain.pem cert.pem
# Output "OK" means chain is valid
# Verify certificate matches private key
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in private.key | openssl md5
# Both should output the same hash
# Extract certificate details
openssl x509 -in cert.pem -noout -text
# Check certificate expiration
openssl x509 -in cert.pem -noout -dates
# Output:
# notBefore=Jan 1 00:00:00 2024 GMT
# notAfter=Jan 1 23:59:59 2025 GMT
Common Certificate Chain Issues
Issue 1: Missing Intermediate Certificates
Symptoms:
- Browser shows "untrusted certificate" warning
- Certificate validation fails in SSL checkers
- Error message: "certificate chain incomplete"
Solution:
# Ensure fullchain.pem includes intermediates
cat leaf-cert.pem intermediate1.pem intermediate2.pem > fullchain.pem
# For Nginx: use ssl_certificate with fullchain.pem
# For Apache: use SSLCertificateChainFile with chain.pem (without leaf)
# Verify the combined file
openssl verify -CAfile fullchain.pem fullchain.pem
Issue 2: Wrong Certificate Order
Symptoms:
- Some browsers trust, others don't
- Inconsistent certificate validation results
- Intermediate certificate errors
Correct Order:
- Leaf certificate (your domain)
- Intermediate certificates (in order from leaf to root)
- Root certificate (usually omitted from fullchain.pem)
# Verify order - should show unbroken chain
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -text -noout
Issue 3: Expired Intermediate Certificate
Symptoms:
- "certificate has expired" error
- Validation fails even though leaf certificate is valid
- Chrome shows "NET::ERR_CERT_AUTHORITY_INVALID"
Verification:
# Check all certificates in chain
openssl s_client -connect example.com:443 -showcerts | grep -A 1 "subject="
# Check each intermediate's expiration
for cert in leaf.pem intermediate1.pem intermediate2.pem; do
echo "=== $cert ==="
openssl x509 -in $cert -noout -dates
done
Solution: Contact your CA to obtain the updated intermediate certificate with extended validity.
Using Tools for Chain Verification
X.509 Certificate Decoder Tool
The X.509 Certificate Decoder tool (/tools/security/x509-decoder) provides visual analysis of certificate chains:
Features:
- Paste certificate and immediately see all details
- Verify Subject Alternative Names (SANs) completeness
- Check signature algorithm (must be SHA-256 or better, never SHA-1)
- Validate key size (minimum 2048-bit RSA or 256-bit ECDSA)
- Identify certificate authority (Issuer DN)
- Check Key Usage and Extended Key Usage extensions
- Security warnings for weak algorithms or configurations
- Compare certificate details across multiple certificates in chain
Workflow:
- Paste leaf certificate → verify details, expiration, SANs
- Paste each intermediate → verify issuer chain
- Paste root certificate → verify self-signed status
- Cross-reference: Each cert's "Issuer" should match next cert's "Subject"
Multi-Server Deployment Automation
Ansible Playbook for Certificate Deployment
Ansible provides a reliable method to deploy certificates across multiple servers simultaneously.
---
- name: Deploy SSL/TLS Certificates
hosts: webservers
become: yes
gather_facts: yes
vars:
ssl_path: /etc/nginx/ssl
cert_src: /local/path/to/certs
notification_email: [email protected]
tasks:
- name: Create SSL directory
file:
path: "{{ ssl_path }}"
state: directory
mode: '0700'
owner: root
group: root
- name: Backup existing certificates
command: "tar czf /etc/nginx/ssl/backup-$(date +%Y%m%d-%H%M%S).tar.gz {{ ssl_path }}/"
register: backup_result
changed_when: false
- name: Display backup location
debug:
msg: "Backup created at {{ backup_result.stdout }}"
- name: Copy certificate files
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: "{{ item.mode }}"
owner: root
group: root
loop:
- { src: "{{ cert_src }}/fullchain.pem", dest: "{{ ssl_path }}/fullchain.pem", mode: '0644' }
- { src: "{{ cert_src }}/chain.pem", dest: "{{ ssl_path }}/chain.pem", mode: '0644' }
- { src: "{{ cert_src }}/private.key", dest: "{{ ssl_path }}/private.key", mode: '0600' }
no_log: true # Don't log private key content
- name: Verify certificate file permissions
file:
path: "{{ ssl_path }}/private.key"
mode: '0600'
owner: root
group: root
- name: Verify certificate-key pair match
shell: |
CERT_MODULUS=$(openssl x509 -noout -modulus -in {{ ssl_path }}/fullchain.pem | openssl md5)
KEY_MODULUS=$(openssl rsa -noout -modulus -in {{ ssl_path }}/private.key | openssl md5)
if [ "$CERT_MODULUS" != "$KEY_MODULUS" ]; then
echo "ERROR: Certificate and key don't match!"
exit 1
fi
register: modulus_check
changed_when: false
- name: Validate certificate chain
shell: "openssl verify -CAfile {{ ssl_path }}/chain.pem {{ ssl_path }}/fullchain.pem"
register: chain_check
changed_when: false
failed_when: chain_check.rc != 0
- name: Check certificate expiration
shell: |
EXPIRY=$(openssl x509 -in {{ ssl_path }}/fullchain.pem -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
CURRENT_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $CURRENT_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt 0 ]; then
echo "ERROR: Certificate has expired!"
exit 1
fi
echo "Certificate expires in $DAYS_LEFT days"
register: expiry_check
changed_when: false
failed_when: expiry_check.rc != 0
- name: Test Nginx configuration
command: "nginx -t"
register: nginx_test
changed_when: false
failed_when: nginx_test.rc != 0
- name: Reload Nginx
service:
name: nginx
state: reloaded
- name: Wait for service to stabilize
wait_for:
port: 443
state: started
delay: 2
timeout: 10
- name: Verify HTTPS is responding
uri:
url: "https://localhost"
status_code: 200, 301, 302, 304
validate_certs: no
register: https_test
retries: 3
delay: 2
- name: Send success notification
mail:
host: smtp.example.com
port: 25
to: "{{ notification_email }}"
subject: "SSL Certificate deployment successful on {{ inventory_hostname }}"
body: |
Certificate deployment completed successfully.
Server: {{ inventory_hostname }}
Certificate: {{ ansible_host }}
Expires in: {{ expiry_check.stdout }}
Verification:
- Certificate-key match: PASSED
- Chain verification: PASSED
- Nginx configuration: PASSED
- HTTPS connectivity: PASSED
when: nginx_test.rc == 0 and https_test.status_code in [200, 301, 302, 304]
- name: Send failure notification
mail:
host: smtp.example.com
port: 25
to: "{{ notification_email }}"
subject: "SSL Certificate deployment FAILED on {{ inventory_hostname }}"
body: |
Certificate deployment failed!
Server: {{ inventory_hostname }}
Error details:
{{ nginx_test.stderr }}
when: nginx_test.rc != 0
failed_when: true
Key Features:
- Automatic backup before replacing certificates
- Verification of certificate-key pair match
- Chain validation before applying
- Expiration date checking
- Configuration testing before reload
- Health check verification
- Automatic notifications on success/failure
- Rollback capability via backup
Kubernetes Deployment
Kubernetes Secret for Certificate
---
# Create secret from certificate files
apiVersion: v1
kind: Secret
metadata:
name: tls-cert-secret
namespace: production
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-certificate>
tls.key: <base64-encoded-private-key>
---
# Deployment using the certificate secret
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 443
name: https
- containerPort: 80
name: http
volumeMounts:
- name: tls-certs
mountPath: /etc/nginx/ssl
readOnly: true
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: tls-cert-secret
defaultMode: 0600
- name: nginx-config
configMap:
name: nginx-config
---
# Ingress using the certificate secret
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: production
annotations:
cert-manager.io/issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.com
- www.example.com
secretName: tls-cert-secret
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app
port:
number: 8080
cert-manager for Automatic Certificate Management
---
# ClusterIssuer for Let's Encrypt
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
- dns01:
cloudflare:
email: [email protected]
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
---
# Certificate resource (auto-renewed by cert-manager)
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com-cert
namespace: production
spec:
secretName: example-com-tls
duration: 2160h # 90 days
renewBefore: 720h # 30 days
commonName: example.com
dnsNames:
- example.com
- www.example.com
- api.example.com
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
privateKey:
algorithm: RSA
size: 2048
Certificate Content Analysis
Using X.509 Certificate Decoder
The X.509 Certificate Decoder tool provides comprehensive certificate analysis without exposing sensitive data.
Analyzing Certificate Details
Step-by-Step Verification:
-
Extract Certificate from Server:
openssl s_client -connect example.com:443 -servername example.com < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > server-cert.pem -
Paste into X.509 Decoder Tool:
- Navigate to /tools/security/x509-decoder
- Paste certificate PEM content
- Click "Decode Certificate"
-
Verify Key Details:
- Subject DN: Common Name (CN) should match your domain
- Subject Alternative Names: Should include apex domain and www variant
- Issuer DN: Identify certificate authority
- Validity Dates: Ensure "Not After" is future date
- Public Key: Minimum 2048-bit RSA or 256-bit ECDSA
- Signature Algorithm: Must be SHA-256 or SHA-384 (never SHA-1)
Critical Certificate Fields
Key Usage Extension: Should include:
- Digital Signature
- Key Encipherment
Extended Key Usage Extension: Should include:
- Server Authentication (OID 1.3.6.1.5.5.7.3.1)
Fingerprint Verification:
# Get certificate fingerprint
openssl x509 -in cert.pem -noout -fingerprint -sha256
# Output: SHA256 Fingerprint=AA:BB:CC:DD:...
# Compare with trusted source to ensure no MITM substitution
Certificate Transparency Verification
Understanding Certificate Transparency
Certificate Transparency (CT) creates an auditable record of all issued certificates in public logs. This enables detection of unauthorized certificate issuance and mitigates CA compromise threats.
CT Requirement Timeline (2025):
- Firefox 135+ (February 2025): Requires CT logging
- Chrome: Transitioning to static CT API logs (completion 2025)
- All major CAs must log to public CT logs
Using Certificate Transparency Lookup Tool
The Certificate Transparency Lookup tool (/tools/security/certificate-transparency-lookup) enables CT verification without technical expertise.
Discovering Certificates via CT
Step-by-Step Process:
-
Search Your Domain:
- Navigate to /tools/security/certificate-transparency-lookup
- Enter domain (example.com)
- Tool searches CT logs for all certificates
-
Review Results:
- Identify all issued certificates
- Verify each certificate matches your authorization
- Check for unexpected certificates (potential compromise indicator)
-
Analyze Certificate Details:
- Issuer name and CA
- Validity dates
- Subject Alternative Names
- Signed Certificate Timestamps (SCTs)
- CT log sources
-
Export for Inventory:
- Export results to CSV
- Compare against authorized certificate database
- Add to change management system
Verifying SCT (Signed Certificate Timestamp)
SCTs prove a certificate was logged in CT logs:
# Extract SCTs from certificate
openssl x509 -in cert.pem -noout -ocsp_uri
# Get all extensions including CT logs
openssl x509 -in cert.pem -noout -text | grep -A 10 "CT Precertificate"
# Check OCSP stapling includes SCTs
openssl s_client -connect example.com:443 -servername example.com | grep -A 5 "OCSP response:"
SCT Delivery Methods:
- Embedded in Certificate - CA includes SCTs at issuance
- TLS Extension - Server provides SCTs during handshake
- OCSP Stapling - SCTs included in OCSP response
SSL/TLS Protocol Testing
Protocol Version Requirements
2025 Best Practices:
- Required: TLS 1.2 minimum
- Recommended: TLS 1.3 support
- Disabled: SSLv3, TLS 1.0, TLS 1.1 (all deprecated)
Modern Cipher Suite Configuration
Recommended Cipher Suites (2025):
For TLS 1.3:
TLS_AES_128_GCM_SHA256
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
For TLS 1.2:
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
Weak Ciphers to Disable:
- RC4 (completely broken)
- 3DES (insufficient key strength)
- Export ciphers (deliberately weak)
- NULL ciphers (no encryption)
- Ciphers without Forward Secrecy
Testing Cipher Suite Configuration
# Test cipher suites with nmap
nmap --script ssl-enum-ciphers -p 443 example.com
# Test with openssl
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
# Test all protocols
echo "" | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | grep "Protocol"
# Test specific ciphers
openssl ciphers -v 'ECDHE:!EXPORT:!eNULL:!aNULL:!aDSS:!DES:!RC4:!3DES:!DH'
Testing with SSL Labs
For comprehensive testing, use SSL Labs Server Test (https://www.ssllabs.com/ssltest/):
- Enter domain name
- Wait for scan completion (2-5 minutes)
- Review detailed report:
- Certificate validity
- Protocol support
- Cipher suite analysis
- Vulnerability detection
- Overall security grade (A+ to F)
Key Metrics:
- Certificate - Should match domain, proper chain
- Protocol Support - TLS 1.2/1.3 only
- Key Exchange - Must support ECDHE (Forward Secrecy)
- Cipher Strength - 128-bit minimum for bulk encryption
- Security Grade - Target A or A+ grade
Security Headers Validation
Critical Security Headers
HSTS (Strict-Transport-Security)
Forces browsers to only connect via HTTPS:
# Nginx configuration
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
Required Parameters:
max-age: Seconds to enforce HTTPS (31536000 = 1 year minimum)includeSubDomains: Apply policy to all subdomainspreload: Submit to HSTS preload list
HSTS Preload Submission:
- Go to https://hstspreload.org/
- Enter domain
- Requires HSTS header with preload directive
- Requires all subdomains on HTTPS
- Benefits: Browsers never attempt HTTP connections
Content-Security-Policy (CSP)
Restricts resource loading to prevent mixed content:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
upgrade-insecure-requests;
Key Directives:
default-src 'self'- Only allow resources from same originupgrade-insecure-requests- Upgrade HTTP resources to HTTPSblock-all-mixed-content- Block all mixed content (deprecated in favor of upgrade-insecure-requests)
X-Frame-Options
Prevents clickjacking attacks:
X-Frame-Options: SAMEORIGIN
Options:
DENY- Never embed in frameSAMEORIGIN- Allow framing from same originALLOW-FROM uri- Allow framing from specific origin (deprecated)
X-Content-Type-Options
Prevents MIME type sniffing:
X-Content-Type-Options: nosniff
Forces browsers to respect Content-Type headers.
Referrer-Policy
Controls referrer information leakage:
Referrer-Policy: no-referrer-when-downgrade
Options:
no-referrer- Never send referrerno-referrer-when-downgrade- Don't send referrer for downgradesame-origin- Only send to same originstrict-origin-when-cross-origin- Strict privacy
Testing Security Headers
Using Security Headers Analyzer Tool
The Security Headers Analyzer tool provides automated testing:
- Enter domain
- Tool analyzes all security headers
- Review findings:
- Header presence/absence
- Configuration correctness
- Security grade (A-F)
- Implementation recommendations
Command-Line Verification
# Check security headers
curl -I https://example.com | grep -i "strict-transport-security\\|x-frame-options\\|x-content-type-options"
# Check all headers with more detail
curl -I https://example.com
# Expected output:
# Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
# X-Frame-Options: SAMEORIGIN
# X-Content-Type-Options: nosniff
# Content-Security-Policy: upgrade-insecure-requests; ...
Mixed Content Detection & Remediation
Understanding Mixed Content
Mixed content occurs when an HTTPS page loads insecure HTTP resources:
Active Mixed Content (Blocked by browsers):
- Scripts (malicious code injection risk)
- iframes
- Objects/Plugins
- Stylesheets
- XMLHttpRequest/Fetch requests
Passive Mixed Content (Causes warnings):
- Images
- Video/Audio elements
- Links
Detecting Mixed Content
Browser Developer Tools Method
- Open Chrome/Firefox Developer Tools (F12)
- Navigate to the website over HTTPS
- Check Console tab for warnings:
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure resource 'http://resource.com/image.jpg'. This request has been blocked; the content must be served over HTTPS.
Automated Detection
# Using testssl.sh script
./testssl.sh --mixed-content https://example.com
# Using curl and grep
curl -s https://example.com | grep -o 'http://[^"]*' | sort -u
Remediation Strategies
Strategy 1: Update Resource URLs
Before:
<img src="http://images.example.com/photo.jpg" />
<script src="http://cdn.example.com/app.js"></script>
After - Protocol-Relative URLs:
<img src="//images.example.com/photo.jpg" />
<script src="//cdn.example.com/app.js"></script>
After - HTTPS URLs:
<img src="https://images.example.com/photo.jpg" />
<script src="https://cdn.example.com/app.js"></script>
Strategy 2: CSP upgrade-insecure-requests Directive
Automatically upgrade HTTP resources to HTTPS:
Content-Security-Policy: upgrade-insecure-requests;
Nginx:
add_header Content-Security-Policy "upgrade-insecure-requests" always;
Apache:
Header always set Content-Security-Policy "upgrade-insecure-requests"
This directive:
- Upgrades HTTP URLs to HTTPS
- Works transparently for users
- May break if resource doesn't support HTTPS
- Test thoroughly before deploying
Strategy 3: Server-Side URL Rewriting
For external resources, rewrite at proxy level:
# Nginx example
location / {
sub_filter 'http://' 'https://';
sub_filter_once off;
proxy_pass http://backend;
}
Common Issues & Troubleshooting
Issue 1: Certificate Verification Errors
Error: "ERR_CERT_AUTHORITY_INVALID" or "untrusted certificate"
Causes:
- Missing intermediate certificates
- Wrong certificate chain order
- Expired intermediate certificate
- Self-signed root certificate not trusted
Diagnosis:
openssl s_client -connect example.com:443 -showcerts
# Count certificates in output (should be 3+)
openssl s_client -connect example.com:443 -showcerts | grep "subject="
Resolution:
# Obtain complete chain from CA
# Combine certificates in correct order
cat leaf.pem intermediate1.pem intermediate2.pem > fullchain.pem
# Update web server config to use fullchain.pem
# Reload web server
sudo systemctl reload nginx
Issue 2: OCSP Stapling Not Working
Error: OCSP stapling not reported in SSL Labs test
Diagnosis:
# Test OCSP stapling
openssl s_client -connect example.com:443 -showcerts -servername example.com -tlsextdebug 2>/dev/null | grep -A 5 "OCSP"
Resolution for Nginx:
# Verify OCSP configuration
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Reload Nginx
sudo systemctl reload nginx
# Verify OCSP responder is accessible
openssl ocsp -CAfile chain.pem -issuer intermediate.pem -cert cert.pem -url http://ocsp.ca.com -header "HOST" "ocsp.ca.com"
Issue 3: Certificate-Key Mismatch
Error: Certificate and private key don't match
Diagnosis:
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in private.key | openssl md5
# Should output identical hashes
Resolution:
- Verify you're using the correct key file
- Regenerate CSR and request new certificate if key is lost
- Ensure key file wasn't overwritten accidentally
Issue 4: Weak Cipher Suites Enabled
Error: SSL Labs shows weak ciphers (DES, RC4, 3DES)
Diagnosis:
nmap --script ssl-enum-ciphers -p 443 example.com | grep "weak\\|least strength"
Resolution:
Nginx:
# Remove weak ciphers
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
ssl_prefer_server_ciphers off; # Modern approach
Apache:
SSLCipherSuite 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'
SSLHonorCipherOrder off
Validation Checklist
Pre-Deployment Checklist
- Certificate valid for correct domain(s)
- Certificate not expired (check "Not After" date)
- Certificate-key pair matches (modulus hash verification)
- Complete certificate chain obtained from CA
- Intermediate certificates in correct order
- Private key file permissions set to 600 (owner read-write only)
- Certificates backed up securely
- Configuration syntax validated (nginx -t, apache2ctl configtest)
- OCSP responder URL verified and accessible
Post-Deployment Checklist
- HTTPS accessible without browser warnings
- Certificate visible in browser (lock icon present)
- Certificate details match domain and organization
- All SANs working (example.com, www.example.com, api.example.com, etc.)
- HTTP automatically redirects to HTTPS
- HSTS header present and correctly configured
- OCSP stapling working (openssl s_client test)
- Certificate chain complete (openssl s_client -showcerts shows 3+ certificates)
- TLS 1.3 supported for modern clients
- No weak ciphers enabled
- Certificate Transparency verified (SCTs present)
- No mixed content warnings
- All security headers present and correct
- SSL Labs test grade A or A+
- Monitoring alerts configured for expiration
- Renewal process tested and verified
Security Testing Checklist
- Run SSL Labs Server Test - review full report
- Test with testssl.sh for comprehensive vulnerability scan
- Verify no known vulnerabilities (Heartbleed, POODLE, BEAST, etc.)
- Check Certificate Transparency logs for unauthorized issuance
- Verify Certificate Authority matches trusted CA list
- Test Perfect Forward Secrecy (ECDHE support)
- Verify session resumption not using compromised keys
- Check for certificate pinning requirements
- Test from multiple geographic locations if possible
- Verify HSTS preload list eligibility
Operational Checklist
- Backup procedure tested and verified
- Rollback procedure documented and tested
- Change management tickets created and approved
- Incident response playbook updated
- Team trained on certificate operations
- Monitoring dashboards configured
- Alert escalation procedures established
- Documentation updated with new certificate details
- Audit log entries reviewed for compliance
- Stakeholders notified of successful deployment
Conclusion
Proper SSL/TLS certificate installation and validation eliminates the most common sources of HTTPS failures, security vulnerabilities, and downtime. By following this comprehensive guide and leveraging the tool integrations provided, you can:
Prevent Outages: Correct certificate chain configuration ensures zero unexpected downtime from certificate errors.
Strengthen Security: Modern cipher suites, HSTS, CSP, and certificate verification protect against downgrade attacks, mixed content injection, and rogue certificates.
Meet Compliance: Proper certificate management, audit trails, and monitoring satisfy regulatory requirements (PCI-DSS, HIPAA, GDPR).
Automate Operations: Playbooks and scripts reduce manual effort from hours to minutes, enabling rapid certificate updates across hundreds of servers.
Detect Threats: Certificate Transparency monitoring and security testing identify compromised certificates and unauthorized issuance in real-time.
Start with the validation checklist to ensure your current deployments are correct, then implement automation to handle future certificate rotations with confidence.
Additional Resources
Certificate Verification Tools
- X.509 Certificate Decoder - Analyze certificate details
- Certificate CSR Generator - Generate CSRs and verify chains
- Certificate Transparency Lookup - Search CT logs and detect unauthorized certificates
External References
- SSL Labs - Server Test - Comprehensive SSL/TLS testing
- testssl.sh - Command-line SSL testing tool
- Mozilla SSL Configuration Generator - Auto-generate web server configs
- OWASP TLS Cheat Sheet - TLS security best practices
- RFC 9700 - OAuth 2.0 Security Best Current Practice - Latest OAuth standards
Monitoring & Management
- Better Stack SSL Monitoring - Comprehensive monitoring comparison
- TrackSSL - Dedicated SSL certificate monitoring
- cert-manager Documentation - Kubernetes certificate automation
Related Reading
For complete SSL/TLS certificate lifecycle context, see:
- SSL/TLS Certificate Lifecycle Management - Complete 8-stage workflow covering all certificate lifecycle aspects