LETO

SSL 인증서 오류가 뜰 때

브라우저가 띄우는 인증서 오류는 문구마다 원인이 다릅니다. 만료, 이름 불일치, 체인 불완전, 신뢰하지 않는 발급자, 혼합 콘텐츠를 구분하고 openssl s_client 로 실제 상태를 확인하는 방법을 정리했습니다.

작성 최종 수정

인증서 오류는 문구에 원인이 거의 다 들어 있습니다. 날짜 문제인지(NET::ERR_CERT_DATE_INVALID), 이름 문제인지(SSL_ERROR_BAD_CERT_DOMAIN, COMMON_NAME_INVALID), 발급자 신뢰 문제인지(ERR_CERT_AUTHORITY_INVALID)를 먼저 읽고, 그다음 openssl s_client 로 서버가 실제로 내려주는 인증서를 확인하면 두 단계 안에 원인이 확정됩니다. 추측으로 인증서를 다시 발급하기 전에 이 두 단계를 먼저 거치세요.

레토는 독립 SSL 상품을 판매하지 않습니다. CDN 에서 Let's Encrypt 관리형 인증서를 자동 발급하고 갱신하거나 직접 준비한 인증서를 등록할 수 있고, 도메인 포워딩의 HTTPS 사용 을 켜면 첫 접속 때 인증서가 자동 발급됩니다. 서버에 직접 인증서를 올리는 일반적인 방법은 SSL 인증서 적용하기에 있습니다.

오류 문구로 원인 가르기

브라우저 문구뜻원인
NET::ERR_CERT_DATE_INVALID유효 기간을 벗어남인증서 만료, 자동 갱신 실패, 서버 시계 오류
COMMON_NAME_INVALID, ERR_CERT_COMMON_NAME_INVALID접속한 이름이 인증서에 없음www 와 루트 중 한쪽만 발급, 서브도메인 누락, 잘못된 서버로 연결
ERR_CERT_AUTHORITY_INVALID신뢰하는 기관까지 올라가지 못함자체 서명 인증서, 사설 CA, 체인 불완전
SSL_ERROR_UNKNOWN_CA_ALERT 등 앱에서만 실패중간 인증서 누락서버가 체인을 내려주지 않음
ERR_SSL_PROTOCOL_ERROR협상 자체가 실패앞단 설정 오류, 지원 프로토콜 불일치
자물쇠에 경고 표시인증서는 정상페이지 안의 혼합 콘텐츠
가장 먼저
브라우저 오류 코드 전문을 그대로 읽기
다음
openssl s_client 로 실제 인증서와 체인 확인
이름 문제 확인
subjectAltName 목록에 접속 이름이 있는가
체인 문제 확인
Verify return code 가 0 인가

openssl 로 실제 상태를 확인합니다

# 유효 기간, 주체, 인증서에 들어 있는 이름 목록
openssl s_client -connect example.co.kr:443 -servername example.co.kr </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# 체인이 루트까지 이어지는지 (Verify return code 확인)
openssl s_client -connect example.co.kr:443 -servername example.co.kr </dev/null 2>&1 \
  | grep -E 'Verify return code|depth=|subject=|issuer='

# 브라우저 보완 없이 검증되는지 (앱 및 서버 간 호출과 같은 조건)
curl -vI https://example.co.kr 2>&1 | grep -iE 'subject|issuer|SSL certificate|expire'

읽는 법은 간단합니다. notAfter 날짜가 지나 있으면 만료, subjectAltName 에 실제 접속 이름이 없으면 이름 불일치, Verify return code 가 0 (ok) 이 아니면 체인이나 발급자 문제입니다. 여러 이름을 쓰는 서버라면 -servername 값을 바꿔 가며 각각 확인해야 합니다.

오류별 대처

  1. 만료: 갱신이 왜 멈췄는지부터 찾습니다

    인증서를 다시 발급하기 전에 자동 갱신이 실패한 이유를 확인합니다. 검증 경로가 리다이렉트나 방화벽으로 막혔는지, 갱신 스케줄러가 돌고 있는지, 발급 기관을 제한하는 CAA 레코드가 바뀌지 않았는지 순서대로 봅니다. 서버 시계가 크게 틀어져 있으면 멀쩡한 인증서도 만료로 보이므로 시간 동기화도 확인합니다. 원인을 고치지 않고 수동 발급만 하면 다음 주기에 같은 사고가 반복됩니다.

  2. 이름 불일치: www 와 루트 도메인을 함께 넣습니다

    루트 도메인과 www 는 서로 다른 이름이므로 인증서에 둘 다 들어 있어야 합니다. 발급 때 두 이름을 모두 지정하거나, 한쪽만 쓰기로 정했다면 다른 쪽도 인증서를 갖춘 상태에서 리다이렉트로 넘깁니다. 서브도메인이 계속 늘어나는 구조라면 와일드카드 인증서를 쓰되, 와일드카드는 한 단계만 덮는다는 점을 기억하세요.

    접속한 이름이 엉뚱한 서버로 가고 있어서 그 서버의 인증서가 내려오는 경우도 있습니다. 이때는 인증서가 아니라 DNS 문제이므로 사이트가 열리지 않을 때의 A 레코드 확인 단계로 돌아갑니다.

  3. 체인 불완전: 중간 인증서를 함께 설치합니다

    데스크톱 브라우저에서는 정상인데 모바일 앱, curl, 서버 간 호출에서만 실패하면 거의 이 경우입니다. 브라우저는 빠진 중간 인증서를 알아서 내려받아 보완하지만 다른 클라이언트는 그렇게 하지 않습니다.

    해결은 인증기관이 준 중간 인증서를 인증서 본문 뒤에 이어 붙인 전체 체인 파일을 서버에 설정하는 것입니다. 발급 도구가 만들어 주는 fullchain 형태의 파일을 쓰면 됩니다. 설정 뒤 위 Verify return code 가 0 (ok) 으로 바뀌는지 확인합니다.

  4. 신뢰하지 않는 발급자: 인증서 종류를 확인합니다

    자체 서명 인증서나 사내 전용 CA로 발급한 인증서는 공개 브라우저가 신뢰하지 않습니다. 외부 공개 서비스라면 공개 인증기관에서 발급받은 인증서로 바꿔야 하고, 사내 전용 시스템이라면 클라이언트에 사내 루트 인증서를 배포하는 쪽이 맞습니다. 기본 설정 페이지가 그대로 남아 있는 호스팅이나 앞단 장비의 기본 인증서가 내려오는 경우도 같은 증상을 냅니다.

  5. 혼합 콘텐츠: 페이지 안의 http 주소를 찾습니다

    인증서는 정상인데 자물쇠에 경고만 뜨면 페이지가 http:// 주소의 리소스를 불러오는 것입니다. 이미지 같은 수동 콘텐츠는 경고만 뜨지만 스크립트와 스타일 같은 능동 콘텐츠는 차단되어 기능이 깨집니다.

    브라우저 개발자 도구 콘솔에서 차단된 주소를 찾아 https 로 바꿉니다. 직접 고칠 수 없는 외부 리소스라면 자체 서버로 옮겨서 제공하거나 해당 기능을 대체해야 합니다. 데이터베이스에 저장된 본문이나 테마 설정에 http 주소가 박혀 있는 경우가 많으므로 그쪽도 함께 살펴보세요.

CAA 레코드가 발급을 막는 경우

CAA 레코드는 그 도메인의 인증서를 발급할 수 있는 기관을 DNS에 지정하는 레코드입니다. 목록에 없는 기관으로 발급을 시도하면 인증기관이 발급을 거부합니다. 새 인증서가 자꾸 발급되지 않거나 자동 갱신이 특정 시점부터 실패했다면 CAA를 확인하세요.

# 도메인과 상위 도메인의 CAA 확인
dig CAA example.co.kr +short
dig CAA co.kr +short

# 와일드카드 발급은 issuewild 가 따로 적용된다
dig CAA www.example.co.kr +short

발급 기관을 바꿀 때는 CAA도 함께 수정해야 합니다. 문법과 설정 예시는 CAA 레코드 설정하기에 있습니다. 레코드는 그 도메인의 존을 실제로 운영하는 DNS 콘솔에서 고칩니다.

레토 DNS는 CAA 레코드를 지원하지 않습니다. 따라서 레토 네임서버에 존을 둔 도메인에는 우리가 넣은 CAA가 있을 수 없고, 발급을 막는 값은 상위 이름에 있는 CAA뿐이므로 위처럼 상위까지 조회해 보면 됩니다. 존을 외부 DNS 호스팅에 두고 있다면 그 콘솔의 CAA를 고쳐야 하고, 레토 네임서버를 쓰면서 CAA로 발급 기관을 제한해야 한다면 그 존을 CAA를 지원하는 외부 DNS로 위임해야 합니다.

그래도 해결되지 않으면

아래 자료를 정리해 문의로 보내 주시면 추측 없이 확인할 수 있습니다.

  • 브라우저 오류 코드 전문과 접속한 정확한 주소
  • openssl s_client 출력 (subject, issuer, dates, subjectAltName, Verify return code)
  • 브라우저에서는 되고 어디에서 실패하는지 (앱, curl, 서버 간 호출)
  • 인증서를 어디에서 발급했고 어디에 설치했는지 (서버, CDN, 앞단 장비)
  • dig CAA 결과

자주 묻는 질문

중간 인증서를 서버에 함께 설치하지 않은 경우가 대부분입니다. 데스크톱 브라우저는 빠진 중간 인증서를 알아서 내려받아 보완하지만 모바일 앱이나 curl, 서버 간 호출은 보완하지 않아 실패합니다. openssl s_client 출력의 체인이 루트까지 이어지는지 확인하세요.

인증서에 두 이름이 모두 들어 있지 않은 것입니다. 발급 때 www 가 붙은 이름과 붙지 않은 이름을 모두 넣어야 합니다. 한쪽만 쓰기로 정했다면 다른 쪽은 인증서를 갖춘 상태에서 리다이렉트로 넘기세요.

갱신 작업이 조용히 실패한 상태로 방치된 경우가 많습니다. 갱신 때 쓰는 검증 경로가 막혔거나, CAA 레코드가 바뀌었거나, 갱신 스케줄러가 멈춰 있는지 순서대로 확인하세요. 만료 며칠 전에 알려 주는 모니터링을 걸어 두면 같은 사고를 막을 수 있습니다.

독립 SSL 상품은 판매하지 않습니다. 대신 CDN 에서 Let's Encrypt 관리형 인증서를 자동 발급하고 자동 갱신하거나 직접 준비한 인증서를 등록할 수 있고, 도메인 포워딩에서도 HTTPS 사용을 켜면 첫 접속 때 인증서가 자동 발급됩니다.

인증서 자체는 정상이고 페이지 안에서 http 주소의 이미지나 스크립트를 불러오는 혼합 콘텐츠 문제입니다. 스크립트나 스타일 같은 능동 콘텐츠는 차단되어 기능이 깨지기도 합니다. 개발자 도구 콘솔에서 차단된 주소를 찾아 https 로 바꾸세요.

관련 가이드