SSL Sertifika Sorunu: Nginx’te HTTPS Hatalarını Giderme
Sunucunuzda HTTPS bağlantıları çalışmıyor, tarayıcılar “NET::ERR_CERT_INVALID” veya “SSL_ERROR_RX_RECORD_TOO_LONG” hatası veriyor ve müşteriler sizi arıyor. Bu durumu yaşadıysanız, ne kadar stresli olduğunu biliyorsunuzdur. SSL sertifika sorunları Nginx üzerinde hem teşhis etmesi hem de çözmesi zor olabilen problemlerden biridir. Bu yazıda gerçek dünya senaryolarından yola çıkarak, yaygın HTTPS hatalarını nasıl tespit edip çözeceğinizi adım adım anlatacağım.
Önce Durumu Anlamak: Hatanın Kaynağı Nerede?
SSL sorunlarını çözmek için önce hatanın gerçekten nerede olduğunu anlamanız gerekir. Sorun sertifikanın kendisinde mi, Nginx konfigürasyonunda mı, yoksa ara sertifika zincirinde mi? Bunu anlamadan körü körüne konfigürasyon değiştirmek saatlerinizi çalabilir.
İlk adım olarak Nginx error log’una bakın:
tail -f /var/log/nginx/error.log
# veya daha detaylı görmek için
journalctl -u nginx -f --since "1 hour ago"
Sonra sertifika dosyasının kendisini inceleyin:
openssl x509 -in /etc/nginx/ssl/sertifika.crt -text -noout | grep -E "Subject|Issuer|Not Before|Not After|DNS"
Bu komut size sertifikanın kime ait olduğunu, hangi alan adlarını kapsadığını ve geçerlilik tarihlerini gösterir. Çoğu zaman sorun burada hemen görünür: ya sertifika süresi dolmuştur ya da yanlış alan adı için düzenlenmiştir.
Senaryo 1: Süresi Dolmuş Sertifika
En klasik senaryo budur. Özellikle Let’s Encrypt kullanıyorsanız ve otomatik yenileme düzgün çalışmıyorsa, 90 günlük sertifika süresi sessiz sedasız dolabilir.
# Sertifika bitiş tarihini kontrol et
openssl x509 -enddate -noout -in /etc/nginx/ssl/sertifika.crt
# Uzak sunucudan doğrulama (dışarıdan kontrol)
echo | openssl s_client -servername alanadiniz.com -connect alanadiniz.com:443 2>/dev/null | openssl x509 -noout -dates
Çıktı şöyle görünecek:
notBefore=Jan 15 00:00:00 2024 GMT
notAfter=Apr 14 23:59:59 2024 GMT
Eğer notAfter tarihi geçmişse, önceliğiniz sertifikayı yenilemektir. Let’s Encrypt kullanıyorsanız:
# Certbot ile manuel yenileme
certbot renew --force-renewal
# Belirli bir domain için
certbot certonly --nginx -d alanadiniz.com -d www.alanadiniz.com
# Nginx'i yeniden yükle
systemctl reload nginx
Önemli nokta: Certbot yenileme sonrası nginx’i otomatik yeniden yüklemesi için /etc/letsencrypt/renewal/alanadiniz.com.conf dosyasında deploy_hook ayarını kontrol edin. Yoksa sertifika yenilense bile Nginx eski sertifikayı kullanmaya devam eder.
# Certbot timer'ının çalışıp çalışmadığını kontrol et
systemctl status certbot.timer
systemctl list-timers | grep certbot
Senaryo 2: Eksik Ara Sertifika (Intermediate Certificate)
Bu hata genellikle şu şekilde karşınıza çıkar: Chrome’da site düzgün açılırken Android veya eski iOS cihazlarda “Güvenilir olmayan sertifika” hatası alırsınız. Ya da curl komutundan hata gelir ama tarayıcıda görünmez.
Sorun, ara sertifika zincirinin (certificate chain) eksik olmasıdır. Nginx’e sadece domain sertifikasını verip CA zincirini vermemişsinizdir.
# Sertifika zincirini kontrol et
openssl s_client -connect alanadiniz.com:443 -showcerts 2>/dev/null | grep -E "subject|issuer"
# Daha detaylı analiz
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt /etc/nginx/ssl/sertifika.crt
Eğer çıktıda yalnızca bir sertifika görüyorsanız veya “unable to get local issuer certificate” hatası alıyorsanız, zincir eksiktir.
Çözüm olarak tam zincir dosyası oluşturmanız gerekir:
# Sertifikaları doğru sırayla birleştir (domain cert + intermediate + root)
cat sertifika.crt intermediate.crt > /etc/nginx/ssl/fullchain.crt
# Let's Encrypt kullanıyorsanız zaten hazır
# /etc/letsencrypt/live/alanadiniz.com/fullchain.pem dosyasını kullanın
Nginx konfigürasyonunda şu şekilde tanımlamanız gerekir:
server {
listen 443 ssl;
server_name alanadiniz.com www.alanadiniz.com;
# Tam zincir dosyasını kullan, sadece domain sertifikasını değil
ssl_certificate /etc/nginx/ssl/fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
}
Senaryo 3: Özel Anahtar ve Sertifika Eşleşmiyor
Bu hata Nginx başlatılırken genellikle şu mesajla ortaya çıkar:
SSL_CTX_use_PrivateKey_file("/etc/nginx/ssl/private.key") failed
(SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)
Sertifika yenilenmiş ama private key güncellenmemiş ya da yanlış key dosyası kullanılıyor demektir.
# Sertifika ve private key'in modülüslerini karşılaştır
openssl x509 -noout -modulus -in /etc/nginx/ssl/sertifika.crt | md5sum
openssl rsa -noout -modulus -in /etc/nginx/ssl/private.key | md5sum
İki çıktı aynıysa eşleşiyorlar demektir. Farklıysa yanlış key kullanıyorsunuzdur. Bu durumda sertifikanın orijinal CSR’ını oluşturduğunuz key dosyasını bulmanız gerekir.
# Sistemde hangi key dosyaları var?
find /etc/nginx /etc/ssl /root -name "*.key" -o -name "*.pem" 2>/dev/null | xargs -I{} sh -c 'echo "=== {} ==="; openssl rsa -noout -modulus -in {} 2>/dev/null | md5sum'
Senaryo 4: HTTP/HTTPS Redirect Döngüsü
“Too many redirects” hatası alıyorsunuz ve sayfaya erişemiyorsunuz. Bu genellikle Nginx’in arkasında bir load balancer veya CDN olduğunda yaşanır.
Tarayıcıdan F12 ile network sekmesine bakın. 301/302 yönlendirmelerin sürekli tekrar ettiğini göreceksiniz.
Senaryo şöyle oluşur: CDN (CloudFlare gibi) sunucuya HTTP olarak bağlanıyor, Nginx bunu görüp HTTPS’e yönlendiriyor, CDN tekrar HTTP bağlanıyor, döngü başlıyor.
# Mevcut durumu curl ile test et
curl -I -L http://alanadiniz.com 2>&1 | head -50
# CloudFlare veya proxy arkasındaki gerçek protokolü anlamak için
curl -H "X-Forwarded-Proto: https" http://alanadiniz.com -I
Nginx konfigürasyonunu düzeltmek için:
server {
listen 80;
server_name alanadiniz.com www.alanadiniz.com;
# Proxy arkasındaysa X-Forwarded-Proto header'ını kontrol et
if ($http_x_forwarded_proto = "https") {
# Zaten HTTPS, yönlendirme yapma
break;
}
# Sadece gerçekten HTTP geliyorsa yönlendir
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name alanadiniz.com;
# Proxy'den gelen gerçek IP ve protokol bilgisi
set_real_ip_from 103.21.244.0/22;
real_ip_header X-Forwarded-For;
ssl_certificate /etc/letsencrypt/live/alanadiniz.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/alanadiniz.com/privkey.pem;
}
Senaryo 5: SNI Sorunu – Yanlış Sertifika Sunuluyor
Aynı sunucuda birden fazla domain barındırıyorsunuz ve bazı domainler için yanlış sertifika sunuluyor. Bu SNI (Server Name Indication) yanlış konfigürasyonundan kaynaklanır.
# Hangi sertifikanın sunulduğunu test et
openssl s_client -servername alanadiniz.com -connect sunucu_ip:443 2>/dev/null | openssl x509 -noout -subject
# Farklı domainler için test
for domain in alanadiniz.com www.alanadiniz.com api.alanadiniz.com; do
echo -n "$domain: "
echo | openssl s_client -servername $domain -connect $domain:443 2>/dev/null | openssl x509 -noout -subject 2>/dev/null
done
Nginx’te her server bloğunun doğru server_name ve sertifika tanımına sahip olduğundan emin olun:
# Varsayılan server bloğu - bilinmeyen domainler için
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/nginx/ssl/default.crt;
ssl_certificate_key /etc/nginx/ssl/default.key;
return 444; # Bağlantıyı kapat
}
# Birinci domain
server {
listen 443 ssl;
server_name site1.com www.site1.com;
ssl_certificate /etc/letsencrypt/live/site1.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site1.com/privkey.pem;
}
# İkinci domain
server {
listen 443 ssl;
server_name site2.com www.site2.com;
ssl_certificate /etc/letsencrypt/live/site2.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site2.com/privkey.pem;
}
Nginx SSL Konfigürasyonunu Doğrulama
Herhangi bir değişiklik yapmadan önce ve sonra konfigürasyonu doğrulamak hayat kurtarır:
# Konfigürasyonu test et
nginx -t
# Daha detaylı çıktı için
nginx -T | grep -A5 "ssl"
# Nginx'i güvenli yeniden yükleme
nginx -t && systemctl reload nginx
SSL konfigürasyon sağlığını test etmek için ssllabs.com API’sini komut satırından kullanabilirsiniz:
# SSL Labs API ile hızlı test (sonuç birkaç dakika sürer)
curl -s "https://api.ssllabs.com/api/v3/analyze?host=alanadiniz.com&startNew=on&all=done" | python3 -m json.tool | grep -E "grade|status"
# Yerel test için nmap ssl script
nmap --script ssl-cert,ssl-enum-ciphers -p 443 alanadiniz.com
Log Analizi: Hataları Doğru Okumak
Nginx SSL loglarında karşılaşabileceğiniz yaygın hata mesajları ve anlamları:
- “SSL_do_handshake() failed”: Client ile sunucu arasında SSL el sıkışması başarısız. Genellikle uyumsuz TLS versiyonları.
- “no required SSL certificate was sent”: Client sertifikası bekleniyor ama gönderilmemiş (mutual TLS senaryosu).
- “SSL_CTX_use_certificate_file failed”: Sertifika dosyası okunamıyor, izin sorununa bakın.
- “PEM_read_bio_X509_AUX() failed”: Sertifika dosyası bozuk veya format yanlış.
# SSL hatalarını filtrele
grep -i "ssl|tls|certificate|handshake" /var/log/nginx/error.log | tail -50
# Zaman bazlı analiz
awk '/SSL.*failed/' /var/log/nginx/error.log | awk '{print $1, $2}' | sort | uniq -c | sort -rn | head -20
# Hangi IP'lerden SSL hatası geliyor?
grep "SSL_do_handshake() failed" /var/log/nginx/error.log | grep -oP 'client: K[0-9.]+' | sort | uniq -c | sort -rn
Mixed Content ve HSTS Sorunları
HTTPS’e geçtiniz ama sayfada bazı elementler hala HTTP üzerinden yüklenmeye çalışıyor. Tarayıcı konsolunda “Mixed Content” uyarıları var.
server {
listen 443 ssl;
server_name alanadiniz.com;
# HSTS header - 1 yıl boyunca tarayıcı zorla HTTPS kullanır
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Content Security Policy ile mixed content'i engelle
add_header Content-Security-Policy "upgrade-insecure-requests" always;
# X-Content-Type-Options
add_header X-Content-Type-Options nosniff always;
ssl_certificate /etc/letsencrypt/live/alanadiniz.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/alanadiniz.com/privkey.pem;
location / {
proxy_pass http://backend;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
}
}
Dikkat: HSTS’i etkinleştirmeden önce HTTPS’inizin düzgün çalıştığından %100 emin olun. Sonradan geri dönmek çok zordur, tarayıcılar bu ayarı cache’ler.
Otomatik Sertifika İzleme
Sertifika sorunlarını önceden önlemek için izleme kurmanız şarttır. Basit bir bash scripti ile bunu yapabilirsiniz:
#!/bin/bash
# ssl_check.sh - Sertifika bitiş tarihi kontrolü
DOMAINS=("alanadiniz.com" "www.alanadiniz.com" "api.alanadiniz.com")
UYARI_GUN=30
EMAIL="[email protected]"
for domain in "${DOMAINS[@]}"; do
# Sertifika bitiş tarihini al
bitis_tarihi=$(echo | openssl s_client -servername $domain -connect $domain:443 2>/dev/null
| openssl x509 -noout -enddate 2>/dev/null
| cut -d= -f2)
if [ -z "$bitis_tarihi" ]; then
echo "HATA: $domain - Sertifika alınamadı!"
echo "$domain sertifikasına erişilemiyor!" | mail -s "SSL HATA: $domain" $EMAIL
continue
fi
# Kalan gün sayısını hesapla
bitis_epoch=$(date -d "$bitis_tarihi" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$bitis_tarihi" +%s)
simdi_epoch=$(date +%s)
kalan_gun=$(( ($bitis_epoch - $simdi_epoch) / 86400 ))
echo "$domain: $kalan_gun gün kaldı (Bitiş: $bitis_tarihi)"
if [ $kalan_gun -le $UYARI_GUN ]; then
echo "UYARI: $domain sertifikası $kalan_gun gün içinde dolacak!" |
mail -s "SSL UYARI: $domain ($kalan_gun gün)" $EMAIL
fi
done
# Script'i çalıştırılabilir yap ve cron'a ekle
chmod +x /usr/local/bin/ssl_check.sh
# Her gün sabah 8'de çalıştır
echo "0 8 * * * root /usr/local/bin/ssl_check.sh >> /var/log/ssl_check.log 2>&1" >> /etc/cron.d/ssl-monitor
TLS Versiyon ve Cipher Suite Sorunları
Eski clientlar veya güvenlik taramaları bazen TLS versiyon uyumsuzluğu nedeniyle bağlanamaz. Özellikle kurumsal ortamlarda eski TLS 1.0/1.1 kullanan sistemlerle karşılaşabilirsiniz.
# Sunucunun desteklediği TLS versiyonlarını test et
nmap --script ssl-enum-ciphers -p 443 alanadiniz.com 2>/dev/null | grep -E "TLS|cipher"
# Belirli TLS versiyonunu zorla test et
openssl s_client -connect alanadiniz.com:443 -tls1_2 2>&1 | grep -E "Protocol|Cipher"
openssl s_client -connect alanadiniz.com:443 -tls1_3 2>&1 | grep -E "Protocol|Cipher"
Modern ve güvenli bir Nginx SSL konfigürasyonu:
# /etc/nginx/snippets/ssl-params.conf
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# Mozilla Modern konfigürasyonu
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_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
Wildcard Sertifika Sorunları
*.alanadiniz.com wildcard sertifikanız var ama alanadiniz.com (kök domain) için çalışmıyor. Wildcard sertifikalar sadece bir seviye alt domainleri kapsar, kök domaini ve iki seviye alt domainleri kapsamaz.
Yani *.alanadiniz.com sertifikası:
- Kapsar:
www.alanadiniz.com,api.alanadiniz.com,mail.alanadiniz.com - Kapsamaz:
alanadiniz.com,sub.sub.alanadiniz.com
# Sertifikanın hangi domainleri kapsadığını kontrol et
openssl x509 -in /etc/nginx/ssl/wildcard.crt -text -noout | grep -A5 "Subject Alternative Name"
Çözüm olarak hem kök domain hem wildcard için SAN (Subject Alternative Name) içeren sertifika almanız gerekir. Let’s Encrypt ile:
certbot certonly --nginx
-d alanadiniz.com
-d "*.alanadiniz.com"
--preferred-challenges dns-01
Sonuç
SSL sorunları panikle değil, sistematik bir yaklaşımla çözülür. Önce openssl komutlarıyla sertifikanın kendisini inceleyin, sonra Nginx error loglarına bakın, ardından konfigürasyonu doğrulayın. Çoğu sorun süresi dolmuş sertifika, eksik zincir ya da private key uyumsuzluğundan kaynaklanır ve bunların hepsi birkaç dakikada çözülebilir.
Reaktif olmak yerine proaktif olmak için sertifika izleme scriptini mutlaka kurun. 30 gün önceden uyarı almanız, saat 2’de telefon çalarken paniklemekten çok daha iyidir. HSTS ve OCSP Stapling gibi güvenlik başlıklarını da ihmal etmeyin, bu ayarlar hem güvenliği artırır hem de performansı iyileştirir.
Son olarak, tüm SSL değişikliklerini bir staging ortamında test etme alışkanlığı edinin. Production’da nginx -t çalıştırmadan reload yapmayın. Bu basit kural sizi çok büyük sorunlardan korur.
