SSL/TLS is the foundation of web security. It encrypts data in transit between the browser and your server, preventing eavesdropping, tampering, and impersonation. Google uses HTTPS as a ranking signal, and browsers now show prominent "Not Secure" warnings for HTTP pages.
In our analysis of 50,000+ scans, 12% of websites had at least one SSL/TLS issue — most commonly mixed content (8%), weak cipher suites (4%), and TLS 1.0/1.1 still enabled (3%).
Also see: Security Headers Checklist 2026, Pre-Launch Security Checklist, Website Security Checklist 2026, and Security Headers Guide.
Why SSL/TLS Matters
- Data protection: Encrypts passwords, credit cards, personal data in transit
- Authentication: Proves your server is who it claims to be
- SEO ranking: Google prioritizes HTTPS sites
- Compliance: PCI DSS 4.0 requires TLS 1.2+ for all payment pages
- Browser trust: HTTP pages show "Not Secure" warnings
Fix: Expired Certificate CRITICAL
An expired certificate causes browsers to show a full-page security warning, blocking most visitors from accessing your site.
# Check certificate expiration
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates
# Renew with Let's Encrypt (Certbot)
sudo certbot renew
# Force renewal (if not yet due)
sudo certbot renew --force-renewal
# Auto-renewal cron job (runs twice daily)
echo "0 0,12 * * * root certbot renew --quiet" | sudo tee /etc/cron.d/certbot-renew
# Restart web server after renewal
sudo systemctl reload apache2 # Apache
sudo systemctl reload nginx # Nginx
Fix: Mixed Content HIGH
Mixed content occurs when an HTTPS page loads resources (images, scripts, stylesheets) over HTTP. Browsers block mixed active content (scripts) and warn about mixed passive content (images).
Quick Fix: Upgrade-Insecure-Requests Header
# Apache — Auto-upgrade HTTP resources to HTTPS
Header always set Content-Security-Policy "upgrade-insecure-requests"
# Nginx
add_header Content-Security-Policy "upgrade-insecure-requests" always;
Permanent Fix: Update All Resource URLs
# Search for HTTP references in your codebase
grep -rn "http://" --include="*.php" --include="*.html" --include="*.css" --include="*.js" .
# Common patterns to fix:
# <img src="http://..." → <img src="https://..."
# <script src="http://..." → <script src="https://..."
# <link href="http://..." → <link href="https://..."
# url("http://...") → url("https://...")
# background: url(http://...) → background: url(https://...)
# WordPress — Update URLs in database
wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' --all-tables
Fix: Disable TLS 1.0/1.1 CRITICAL
TLS 1.0 (1999) and TLS 1.1 (2006) have known vulnerabilities (BEAST, POODLE) and are prohibited by PCI DSS 4.0. All modern browsers support TLS 1.2+.
# Apache — TLS 1.2+ only
SSLProtocol -all +TLSv1.2 +TLSv1.3
# Nginx — TLS 1.2+ only
ssl_protocols TLSv1.2 TLSv1.3;
# Verify TLS versions
# Check if TLS 1.0 is disabled (should fail):
openssl s_client -connect yourdomain.com:443 -tls1 2>&1 | grep -i "handshake"
# Check if TLS 1.1 is disabled (should fail):
openssl s_client -connect yourdomain.com:443 -tls1_1 2>&1 | grep -i "handshake"
# Check if TLS 1.2 works (should succeed):
openssl s_client -connect yourdomain.com:443 -tls1_2 2>&1 | grep -i "handshake"
Fix: Weak Cipher Suites HIGH
Weak ciphers (RC4, DES, 3DES, export ciphers) can be broken by attackers. Use only modern, strong cipher suites.
# Apache — Strong cipher configuration (Mozilla Intermediate)
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
# Nginx — Strong cipher configuration
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
Fix: Certificate Chain Issues HIGH
An incomplete certificate chain means the server isn't sending intermediate certificates, causing trust errors on some devices.
# Check certificate chain
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | grep -A 1 "Certificate chain"
# Fix: Concatenate certificates in correct order
# Order: Your cert → Intermediate cert(s) → Root cert (optional)
cat your_domain.crt intermediate.crt > fullchain.crt
# Apache
SSLCertificateFile /etc/ssl/certs/your_domain.crt
SSLCertificateChainFile /etc/ssl/certs/intermediate.crt
# Or use fullchain:
SSLCertificateFile /etc/ssl/certs/fullchain.crt
# Nginx
ssl_certificate /etc/ssl/certs/fullchain.crt;
ssl_certificate_key /etc/ssl/private/your_domain.key;
Fix: HTTP to HTTPS Redirect HIGH
# Apache — .htaccess
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Nginx
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
# Also add HSTS to prevent future HTTP requests
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Free SSL with Let's Encrypt
# Install Certbot
sudo apt update
sudo apt install certbot
# Apache
sudo apt install python3-certbot-apache
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
# Nginx
sudo apt install python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Standalone (no web server integration)
sudo certbot certonly --standalone -d yourdomain.com
# Wildcard certificate (requires DNS validation)
sudo certbot certonly --manual --preferred-challenges dns -d "*.yourdomain.com" -d yourdomain.com
# Test auto-renewal
sudo certbot renew --dry-run
Testing Your SSL Configuration
# Quick SSL check
curl -vI https://yourdomain.com 2>&1 | grep -E "(SSL|TLS|certificate|expire)"
# Detailed check with openssl
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com
# Check supported protocols
nmap --script ssl-enum-ciphers -p 443 yourdomain.com
For a comprehensive check, use AI QA Monkey's free security scanner — it validates your SSL certificate, checks TLS versions, cipher suites, HSTS, and mixed content in one scan.
HSTS: preventing protocol downgrade attacks
HTTP Strict Transport Security (HSTS) instructs browsers to only connect to your site over HTTPS, even if the user types http://. Without HSTS, attackers on a shared network can intercept the initial HTTP request before it redirects to HTTPS — a classic SSL stripping attack.
# Apache — set HSTS with 1-year max-age
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# The three components:
# max-age=31536000 — Browsers remember HTTPS-only for 1 year
# includeSubDomains — Applies to all subdomains too
# preload — Submit domain to browser HSTS preload list (optional but recommended)
- Start with a shorter max-age: When first deploying HSTS, use
max-age=300(5 minutes) to verify no HTTPS issues exist before committing to a year-long policy. - includeSubDomains risk: If any subdomain does not support HTTPS,
includeSubDomainswill break it. Confirm all subdomains are HTTPS-ready before adding this flag. - HSTS preload list: Submitting to the preload list (at hstspreload.org) means your domain is hardcoded in Chrome, Firefox, Safari, and Edge to use HTTPS even on the first visit — before any header is seen.
Certificate types: DV vs. OV vs. EV
Not all SSL certificates provide the same level of assurance. Understanding the difference helps in choosing the right certificate for your use case.
- DV (Domain Validated): Verifies only that the applicant controls the domain. Issued in minutes. Free via Let's Encrypt. Suitable for most websites, blogs, and web applications. Shows a padlock in the browser but no organization name.
- OV (Organization Validated): Verifies the organization behind the domain through business registration checks. Takes 1-3 days. Shows the organization name in certificate details. Appropriate for businesses that want to demonstrate a verified entity behind the certificate.
- EV (Extended Validation): Highest level of verification — rigorous identity checks, legal registration, and physical presence confirmation. Takes up to a week. Historically showed a green address bar; modern browsers have removed this visual distinction but EV certificates still provide the highest assurance in certificate details. Required for some regulated industries.
- Wildcard certificates: Cover a domain and all immediate subdomains (
*.yourdomain.com). Useful when managing multiple subdomains. Note: wildcards do not cover second-level subdomains (*.sub.yourdomain.com) — a separate wildcard or SAN certificate is required.
Certificate transparency and CAA records
Two often-overlooked SSL hardening steps that significantly reduce the risk of certificate mis-issuance:
- Certificate Transparency (CT) monitoring: All publicly trusted certificates are logged in public CT logs. Services like crt.sh let you monitor all certificates issued for your domain. Set up alerts for unexpected certificate issuance — a certificate you did not request could indicate a DNS hijack or CA compromise.
- CAA DNS records: Certification Authority Authorization records in DNS restrict which CAs are permitted to issue certificates for your domain. Without a CAA record, any CA can issue a certificate. With one, only listed CAs can issue:
# CAA record examples (add in DNS provider) yourdomain.com. CAA 0 issue "letsencrypt.org" yourdomain.com. CAA 0 issue "digicert.com" yourdomain.com. CAA 0 issuewild "letsencrypt.org" # Wildcard permission yourdomain.com. CAA 0 iodef "mailto:security@yourdomain.com" # Violation reports
Common SSL issues after migration
HTTPS migrations (moving from HTTP to HTTPS or migrating hosts) introduce a predictable set of SSL issues. Checking these after every migration prevents post-launch surprises:
- Certificate not covering all domains: If you use both
yourdomain.comandwww.yourdomain.com, confirm both are covered by the certificate's Subject Alternative Names (SAN). Let's Encrypt includes both when specified with-d yourdomain.com -d www.yourdomain.com. - Hardcoded HTTP URLs in database: WordPress and other CMS platforms often store URLs in the database. After HTTPS migration, run a search-replace on the database to update all
http://yourdomain.comreferences tohttps://yourdomain.com. Usewp search-replaceor a database migration tool. - Third-party scripts still HTTP: Analytics, chat widgets, ad scripts, and CDN assets may load via HTTP. Update every third-party script URL to HTTPS or use the
upgrade-insecure-requestsCSP directive as a temporary measure. - Load balancer not forwarding HTTPS: When using a load balancer or CDN that terminates SSL, ensure the backend receives the
X-Forwarded-Proto: httpsheader and your application respects it for HTTPS detection and redirect logic.
Check Your SSL/TLS Configuration
Free scan — validates certificate, TLS version, cipher suites, HSTS, and mixed content.
Scan Your SSL NowFrequently Asked Questions
How do I fix an expired SSL certificate?
Renew through your CA or use Let's Encrypt: sudo certbot renew. Set up a cron job for automatic renewal.
How do I fix mixed content warnings?
Add the Content-Security-Policy: upgrade-insecure-requests header for a quick fix, then update all resource URLs from http:// to https://.
How do I disable TLS 1.0 and 1.1?
Apache: SSLProtocol -all +TLSv1.2 +TLSv1.3. Nginx: ssl_protocols TLSv1.2 TLSv1.3;