갱신은 분명히 됐는데 왜 아직도 만료라고 뜨는가
새벽 2시에 흔히 벌어지는 장면이 있다. certbot renew 로그에는 Congratulations, all renewals succeeded가 찍혀 있고, ls -l /etc/letsencrypt/live/example.com/을 보면 파일 타임스탬프도 방금 전이다. 그런데 브라우저는 여전히 NET::ERR_CERT_DATE_INVALID를 띄운다.
여기서 반드시 잡고 가야 할 전제가 하나 있다.
디스크의 인증서 파일과, 서버 프로세스가 메모리에 물고 있는 인증서는 완전히 별개의 물건이다.
nginx나 haproxy는 기동/reload 시점에 인증서 파일을 읽어 메모리에 올린다. 그 이후 파일이 바뀌어도 프로세스는 알지 못한다. 그래서 "파일을 확인했다"는 것은 진단이 아니다. 진단은 소켓에서 서버가 실제로 무엇을 내려주는지 확인하는 것이다.
이 글의 범위는 명확하다. 서버가 내려주는 인증서의 만료·체인 구성·프로토콜 협상 실패만 다룬다. 클라이언트 쪽 신뢰 저장소나 CA 번들 문제(사내 루트 CA 미설치, JDK cacerts, 파이썬 certifi 등)는 원인 계층이 다르므로 해당 지점에서 링크만 건다.
적용 범위는 다음과 같다.
| 항목 | 범위 |
|---|---|
| OS | Linux 일반 (RHEL/Rocky 8 |
| 서버 | nginx 1.18+, haproxy 2.4+ |
| 도구 | OpenSSL 1.1.1 / 3.x, curl 7.x+, certbot / acme.sh |
| 제외 | 클라이언트 트러스트스토어, 사내 CA 배포, mTLS 클라이언트 인증서 |
에러 원문 → 원인 판정표 (여기서 30초 안에 끝난다)
스크롤하지 말고 지금 보고 있는 에러 문자열을 왼쪽 열에서 찾는다.
| 에러 원문 | 유력 원인 | 함께 나타나야 하는 증상(오판 차단) |
|---|---|---|
certificate has expired / ERR_CERT_DATE_INVALID / Verify return code: 10 | (a) notAfter 실제 경과 — 갱신 자체가 안 됨 | 파일 fingerprint와 소켓 fingerprint가 동일. certbot 로그에 실패 흔적 |
| 위와 같은 에러인데 파일은 최신 | (b) reload 누락 | 파일과 소켓 fingerprint가 다름. ps -o lstart 기동 시각 < 인증서 갱신 시각 |
unable to get local issuer certificate / Verify return code: 21 (특정 클라이언트만) | (c) 중간 CA 체인 누락 | 브라우저는 정상(AIA 보정), curl·Java·모바일만 실패. Certificate chain의 depth 1이 없음 |
sslv3 alert handshake failure / SSL_ERROR_SYSCALL / nginx error.log의 no shared cipher | (d) 프로토콜·암호군 불일치 | 인증서 날짜는 정상. 특정 클라이언트·특정 TLS 버전에서만 실패 |
certificate is not yet valid / notBefore가 미래 | (e) 시계 오차 또는 체인 상단 교체 | date -u가 실제 시각과 어긋남, 또는 컨테이너 시계 드리프트 |
각 분기의 확정 근거는 아래 진단 명령 세트에서 한 번에 나온다. 추측으로 certbot renew를 다시 돌리는 것은 (b)~(e) 상황에서 아무것도 고치지 못하고 rate limit만 소모한다.
30초 진단 명령 세트 — 출력의 어느 줄을 보는가
1) 소켓에서 실제 체인 확인
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | head -40정상(체인 완전) 출력 예시:
Certificate chain
0 s:CN = example.com
i:C = US, O = Let's Encrypt, CN = R11
1 s:C = US, O = Let's Encrypt, CN = R11
i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
---
Verify return code: 0 (ok)depth 1 누락 출력 예시:
Certificate chain
0 s:CN = example.com
i:C = US, O = Let's Encrypt, CN = R11
---
Verify return code: 21 (unable to verify the first certificate)읽는 법은 단순하다.
- depth 0 = 서버 인증서(리프)
- depth 1 = 중간 CA
- depth 2 = 루트(보통 생략되며, 생략이 정상)
depth 1이 통째로 비어 있으면 ssl_certificate에 fullchain.pem이 아니라 cert.pem을 넣었을 가능성이 압도적으로 높다. 이게 (c) 분기다.
Verify return code 두 값의 차이를 혼동하면 30분이 날아간다.
| 코드 | 의미 | 조치 방향 |
|---|---|---|
10 (certificate has expired) | 날짜 문제 — (a) 또는 (b) | 갱신 여부 + reload 여부 확인 |
21 (unable to verify the first certificate) | 체인 문제 — (c) | fullchain 지정 교정 |
0 (ok) | 서버 측 정상 | 이후는 클라이언트 신뢰 저장소 영역 |
2) 서버가 내려준 날짜만 딱 뽑기
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -dates -subject -issuer예상 정상 결과:
notBefore=Aug 20 03:11:02 2026 GMT
notAfter=Nov 18 03:11:01 2026 GMT
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R11핵심은 이 값이 파일이 아니라 소켓 기준이라는 점이다. notAfter가 과거면 (a) 또는 (b), notBefore가 미래면 (e)다.
3) curl로 교차 확인
curl -vI https://example.com 2>&1 | grep -Ei 'expire date|start date|SSL certificate|issuer'SSL certificate problem: unable to get local issuer certificate가 나오는데 브라우저에서는 멀쩡하다면 거의 확정적으로 (c)다. Chrome·Edge·Safari 등은 AIA(Authority Information Access) 주소에서 누락된 중간 CA를 내려받아 보정하고, Firefox는 미리 내려받아 둔 중간 CA 목록으로 보정한다. Firefox intermediate preloading curl·Java·구형 모바일 스택은 이런 보정을 하지 않으므로 서버가 체인을 보내야 한다.
4) 실제 서빙 중인 인증서 경로 확정
nginx -T 2>/dev/null | grep -nE 'server_name|ssl_certificate' | head -40nginx -T는 include된 모든 설정을 펼쳐서 보여준다. sites-enabled에 예전 vhost가 남아 있어 엉뚱한 경로를 물고 있는 경우가 자주 보고된다.
5) reload 누락 확정 — 파일 vs 소켓 fingerprint 대조
이 두 줄이 이 글에서 가장 실전적인 기법이다.
# 파일 쪽 지문
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -fingerprint -sha256
# 소켓 쪽 지문
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -fingerprint -sha256- 두 값이 같다 → 서버는 파일대로 서빙 중. 만료라면 갱신 자체가 실패한 (a)
- 두 값이 다르다 → 파일은 새것, 프로세스는 옛것. (b) reload 누락 확정. 더 볼 것 없다
보강 근거는 프로세스 기동 시각이다.
ss -tlnp | grep :443
PID=$(pgrep -f 'nginx: master' | head -1)
ps -o pid,lstart,cmd -p "$PID"
stat -L -c '%n %y' /etc/letsencrypt/live/example.com/fullchain.pem # -L: 심볼릭 링크가 가리키는 실제 파일 시각ps -o lstart 값이 인증서 파일의 mtime보다 이르면 그 프로세스가 기동 시점에 새 인증서를 읽지 않은 것이다. 단 nginx -s reload·systemctl reload는 마스터 PID와 기동 시각을 바꾸지 않고 설정·인증서만 다시 읽으므로, 이 비교는 보조 근거로만 쓰고 결론은 위의 지문 대조로 낸다. nginx는 인증서를 읽은 뒤 파일을 열어 두지 않으므로 lsof로는 확인할 수 없다(예전 판의 lsof 단계는 삭제했다).
6) no shared cipher 계열 — (d) 확정
nginx error.log에 다음 줄이 있으면 날짜·체인은 잊어도 된다.
SSL_do_handshake() failed (SSL: error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher) # OpenSSL 1.0.x
SSL_do_handshake() failed (SSL: error:0A0000C1:SSL routines::no shared cipher) # OpenSSL 3.x서버의 OpenSSL 버전에 따라 오류 코드 표기가 다르므로 no shared cipher 문자열로 찾는다.
지원 프로토콜을 직접 훑어 어디서 끊기는지 본다.
for p in tls1_2 tls1_3; do
printf '%-8s ' "$p"
openssl s_client -brief -connect example.com:443 -servername example.com -$p </dev/null 2>&1 \
| grep -q 'CONNECTION ESTABLISHED' && echo OK || echo FAIL
done
# TLS 1.0/1.1 지원 여부까지 보려면(OpenSSL 3.x 클라이언트는 보안 수준을 0으로 낮춰야 시도 자체를 함)
for p in tls1 tls1_1; do
printf '%-8s ' "$p"
openssl s_client -brief -connect example.com:443 -servername example.com -$p \
-cipher 'DEFAULT:@SECLEVEL=0' </dev/null 2>&1 \
| grep -q 'CONNECTION ESTABLISHED' && echo OK || echo FAIL
done예전 판의 반복문은 실패해도 출력되는 Cipher is (NONE) 줄까지 성공으로 세어 항상 OK가 나왔다. -brief의 CONNECTION ESTABLISHED로 판정해야 한다. 또 OpenSSL 3.x 클라이언트는 기본 보안 수준(1)에서 TLS 1.2 미만을 아예 쓰지 않으므로, -tls1이 FAIL이어도 서버 탓이 아닐 수 있다. OpenSSL 보안 수준
TLS 1.2/1.3만 OK이고 1.0/1.1이 FAIL이면 그건 정상적인 보안 설정이다. 이때 실패하는 클라이언트는 구형 스택이므로 서버를 낮추기보다 클라이언트를 올리는 게 맞다. 반대로 TLS 1.2도 FAIL이면 ssl_ciphers 설정과 키 타입(RSA/ECDSA) 불일치를 의심한다. ECDSA 전용 cipher suite만 남겨둔 상태에서 RSA 키 인증서를 물리면 no shared cipher가 그대로 터진다.
권장 기준선:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;7) 시계 오차 — (e)
date -u
timedatectl status | grep -E 'System clock|NTP'System clock synchronized: no면 컨테이너/VM 시계 드리프트를 먼저 잡는다. 시계가 미래로 어긋난 서버는 정상 인증서도 certificate has expired로 판정하고, 과거로 어긋나면 certificate is not yet valid를 낸다.
SNI 다중 도메인: '특정 도메인만' 실패할 때
한 IP에 여러 vhost가 붙어 있으면 -servername 유무에 따라 결과가 갈린다.
servername 붙였을 때:
openssl s_client -connect 203.0.113.10:443 -servername shop.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject
# subject=CN = shop.example.com뺐을 때:
openssl s_client -connect 203.0.113.10:443 </dev/null 2>/dev/null \
| openssl x509 -noout -subject
# subject=CN = www.example.com ← default_server의 인증서이 차이가 의미하는 것을 표로 정리한다.
| 관찰 | 해석 | 조치 |
|---|---|---|
| servername 있을 때만 정상 | 정상 동작. SNI 미지원 클라이언트만 실패 | 구형 Android 4.x·Java 6 등 → 클라이언트 업그레이드 또는 전용 IP 분리 |
| servername 넣어도 다른 CN이 나옴 | server_name 오타 또는 vhost 미매칭 → default_server로 흘러감 | nginx -T로 server_name 확인, 와일드카드 포함 여부 점검 |
| CN은 맞는데 브라우저가 이름 불일치 | SAN에 해당 호스트 없음 | 아래 SAN 확인 후 재발급 |
| L4/CDN 뒤에서만 실패 | 앞단이 SNI를 원본에 전달하지 않음 | 프록시의 SNI 전달(proxy_ssl_server_name on 등) 설정 |
SAN 확인:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -ext subjectAltNameX509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.comshop.example.com으로 접속하는데 위 목록에 없으면 그 도메인은 처음부터 이 인증서의 대상이 아니다.
여기까지 왔는데 Verify return code: 0 (ok)이고 SAN도 맞는다면 서버 측 문제는 끝난 것이다. 그런데도 특정 클라이언트만 실패한다면 원인은 클라이언트 신뢰 저장소로 넘어간다. Java·Go 계열은 x509 certificate signed by unknown authority 계열 문제, Python 클라이언트는 SSLCertVerificationError CERTIFICATE_VERIFY_FAILED 원인 5종 판별법을 참고하면 된다.
복구 런북
cert.pem vs fullchain.pem — (c) 원인의 대부분
잘못된 설정:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/cert.pem; # ← 서버 인증서만
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}교정본:
server {
listen 443 ssl;
http2 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # ← 서버+중간 CA
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
}cert.pem은 리프 하나뿐이고 fullchain.pem은 리프 + 중간 CA다. 한 줄 차이로 depth 1이 사라진다.
OCSP stapling은 CA에 따라 다르다. Let's Encrypt는 2025-05-07부터 인증서에 OCSP URL을 넣지 않고 2025-08-06에 OCSP 응답 서버를 껐다. 이후 발급된 Let's Encrypt 인증서에 ssl_stapling on;을 두면 nginx가 OCSP 주소가 없다는 경고를 남기고 stapling을 하지 않는다. 예전 판의 교정본에 있던 ssl_stapling on; ssl_stapling_verify on;은 그래서 뺐다. OCSP를 계속 제공하는 CA의 인증서를 쓸 때만 켜고, 이때는 ssl_trusted_certificate와 resolver도 함께 설정한다. Let's Encrypt OCSP 종료 · nginx ssl_stapling
키-인증서 매칭과 체인 검증
# 키와 인증서가 같은 쌍인지: 공개키 해시 비교 (RSA·ECDSA 공통, 두 해시가 같아야 정상)
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -pubkey | openssl sha256
sudo openssl pkey -in /etc/letsencrypt/live/example.com/privkey.pem -pubout | openssl sha256
# 체인 검증
openssl verify -untrusted /etc/letsencrypt/live/example.com/chain.pem \
/etc/letsencrypt/live/example.com/cert.pem
# 기대 출력: cert.pem: OK예전 판은 openssl rsa -modulus를 썼는데, certbot 2.0부터 새 인증서의 기본 키가 ECDSA(P-256)라 그 명령은 openssl rsa에서 오류가 난다. 위의 -pubkey/pkey -pubout 비교는 키 종류와 상관없이 동작한다. certbot 변경 이력
반영 순서
sudo nginx -t # syntax is ok / test is successful 확인 필수
sudo systemctl reload nginx
# 반영 확인 — 소켓 지문 재확인
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -dates -fingerprint -sha256nginx -t가 실패한 상태에서 reload하면 기존 설정이 유지되어 "고쳤는데 그대로"인 상황이 재현된다. haproxy는 reload 시 소켓 인계 방식에 따라 순간적으로 기존 연결이 유지되므로, 갱신 반영 여부는 반드시 새 연결로 확인한다.
reload를 갱신에 묶기 (정석)
# 1회 실행에 훅 지정 (이번 renew 실행에만 적용된다고 보고, 상시 적용은 아래 훅 파일로)
sudo certbot renew --deploy-hook "systemctl reload nginx"
# 훅 파일로 고정: certbot이 갱신에 성공할 때마다 실행
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/sh
/usr/bin/nginx -t && /bin/systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh훅 파일은 인증서가 실제로 갱신됐을 때만 실행된다. 적용 후 sudo certbot renew --dry-run으로 훅까지 실행되는지 확인하고, 끝나면 소켓 지문을 다시 본다. certbot 사용 문서: 갱신 훅
acme.sh는 --reloadcmd로 같은 역할을 한다.
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.crt \
--reloadcmd "nginx -t && systemctl reload nginx"공인 TLS 인증서의 최대 유효기간은 CA/Browser Forum 투표 SC-081에 따라 발급일 기준 2026-03-15부터 200일, 2027-03-15부터 100일, 2029-03-15부터 47일로 줄어든다. CA/B Forum Ballot SC-081v3 갱신이 잦아질수록 reload 누락 장애가 일어날 기회도 늘어난다. 갱신 성공과 서비스 반영은 별개 이벤트이기 때문이다. 쓰는 CA의 실제 발급 기간은 그 CA의 공지로 확인한다.
롤백
ls -l /etc/letsencrypt/archive/example.com/
# 롤백 전 증적 보존 (필수)
sudo cp -a /etc/letsencrypt/live/example.com /root/incident-$(date +%Y%m%d%H%M)/
sudo journalctl -u nginx --since "1 hour ago" > /root/incident-nginx.log
# 이전 버전으로 심볼릭 링크 되돌리기 (예: 12 → 11): 네 파일을 같은 번호로 함께 바꾼다
N=11
cd /etc/letsencrypt/live/example.com
for f in cert chain fullchain privkey; do
sudo ln -sfn "../../archive/example.com/${f}${N}.pem" "${f}.pem"
done
sudo openssl x509 -in fullchain.pem -noout -enddate # 아직 유효한지 먼저 확인
sudo nginx -t && sudo systemctl reload nginx롤백은 이전 인증서가 아직 유효기간 내일 때만 의미가 있다. 만료된 것으로 되돌리면 상황이 더 나빠진다. 일부 파일만 되돌리면 키와 인증서가 짝이 맞지 않아 nginx가 기동하지 않는다. 다음 certbot renew는 새 번호로 링크를 다시 만들므로, 롤백은 원인을 고칠 때까지의 임시 조치다.
재발 방지 — 파일이 아니라 소켓을 감시한다
파일 mtime을 감시하는 스크립트는 (b) reload 누락을 절대 잡지 못한다. 감시 대상은 반드시 소켓이어야 한다.
#!/usr/bin/env bash
# /usr/local/bin/tls-expiry-check.sh
set -uo pipefail
DOMAINS=(
"example.com:443"
"shop.example.com:443"
"api.example.com:443"
)
THRESHOLD_DAYS="${THRESHOLD_DAYS:-30}"
NOW_EPOCH=$(date +%s)
EXIT_CODE=0
for entry in "${DOMAINS[@]}"; do
host="${entry%%:*}"
port="${entry##*:}"
end_date=$(echo | timeout 10 openssl s_client \
-connect "${host}:${port}" -servername "${host}" 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "${end_date}" ]; then
echo "CRITICAL ${host} - 인증서 조회 실패 (접속/핸드셰이크 오류)"
EXIT_CODE=2
continue
fi
end_epoch=$(date -d "${end_date}" +%s 2>/dev/null) || {
echo "CRITICAL ${host} - 날짜 파싱 실패: ${end_date}"; EXIT_CODE=2; continue; }
days_left=$(( (end_epoch - NOW_EPOCH) / 86400 ))
if [ "${days_left}" -lt 0 ]; then
echo "CRITICAL ${host} - 이미 만료됨 (${end_date})"; EXIT_CODE=2
elif [ "${days_left}" -lt "${THRESHOLD_DAYS}" ]; then
echo "WARNING ${host} - ${days_left}일 남음 (${end_date})"
[ "${EXIT_CODE}" -lt 1 ] && EXIT_CODE=1
else
echo "OK ${host} - ${days_left}일 남음"
fi
done
exit "${EXIT_CODE}"sudo chmod +x /usr/local/bin/tls-expiry-check.sh
/usr/local/bin/tls-expiry-check.sh예상 정상 출력:
OK example.com - 74일 남음
OK shop.example.com - 74일 남음
WARNING api.example.com - 12일 남음 (Sep 13 08:22:10 2026 GMT)종료 코드는 0(정상) / 1(경고) / 2(치명)로 나뉘므로 Nagios·Zabbix 계열이나 사내 알림 스크립트에 그대로 물릴 수 있다.
cron 등록:
# crontab -e
# cron은 셸의 줄 이어쓰기(\)를 지원하지 않는다: 명령은 반드시 한 줄로 쓴다.
# cron 환경에는 로그인 셸의 변수가 없으므로 웹훅 주소를 crontab에 직접 정의한다.
WEBHOOK_URL=https://hooks.example.com/REPLACE_ME
0 9 * * * /usr/local/bin/tls-expiry-check.sh >> /var/log/tls-expiry.log 2>&1 || curl -s -X POST -H 'Content-Type: application/json' -d "{\"text\":\"[TLS] 인증서 만료 경고 - $(hostname)\"}" "$WEBHOOK_URL"systemd service + timer (권장):
# /etc/systemd/system/tls-expiry-check.service
[Unit]
Description=TLS certificate expiry check (socket-based)
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
Environment=THRESHOLD_DAYS=30
ExecStart=/usr/local/bin/tls-expiry-check.sh
StandardOutput=journal
StandardError=journal# /etc/systemd/system/tls-expiry-check.timer
[Unit]
Description=Run TLS expiry check daily
[Timer]
OnCalendar=*-*-* 09:00:00
RandomizedDelaySec=600
Persistent=true
Unit=tls-expiry-check.service
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl enable --now tls-expiry-check.timer
systemctl list-timers tls-expiry-check.timerPersistent=true는 서버가 꺼져 있어 실행 시각을 놓쳤을 때 부팅 직후 한 번 실행해 준다. 배치 서버처럼 상시 가동이 아닌 장비에서 특히 중요하다.
마지막으로 한 문장만 기억하면 된다. 감시 대상은 파일이 아니라 소켓이다. 소켓을 보면 갱신 실패도, reload 누락도, 체인 누락도 한 번에 걸린다.
자주 묻는 질문 (FAQ)
Q. certbot renew가 성공했는데도 브라우저에 만료가 뜹니다. 무엇부터 봐야 하나요?
A. 파일 fingerprint와 소켓 fingerprint를 대조하세요. openssl x509 -in fullchain.pem -noout -fingerprint -sha256 값과 openssl s_client로 받은 fingerprint가 다르면 reload 누락이 확정입니다. nginx -t && systemctl reload nginx로 즉시 해소되며, 재발 방지는 --deploy-hook으로 reload를 갱신에 묶는 것입니다.
Q. 브라우저에서는 정상인데 curl과 Java 앱만 unable to get local issuer certificate가 납니다.
A. 중간 CA 체인 누락(depth 1 없음)일 가능성이 높습니다. 브라우저는 AIA 내려받기나 미리 내려받은 중간 CA 목록으로 누락분을 보정하지만 다른 스택은 그러지 않습니다. nginx ssl_certificate가 cert.pem으로 되어 있지 않은지 확인하고 fullchain.pem으로 교체한 뒤 reload하세요. 그 후에도 Verify return code: 0인데 특정 클라이언트만 실패하면 그때부터는 클라이언트 신뢰 저장소 문제입니다.
Q. nginx error.log에 no shared cipher가 계속 찍힙니다.
A. 인증서 날짜 문제가 아니라 협상 실패입니다. 서버가 TLS 1.0/1.1을 껐는데 구형 클라이언트가 붙는 경우, 또는 ssl_ciphers에 ECDSA 전용 스위트만 남겨둔 채 RSA 키 인증서를 서빙하는 경우가 대표적입니다. -tls1_2, -tls1_3 옵션으로 어느 버전에서 끊기는지 먼저 확정한 뒤 cipher 목록과 키 타입 조합을 맞추세요.
글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보
이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.
댓글
첫 번째 댓글을 남겨보세요.