1. 오류 문구로 유형을 나누고 확인할 계층 정하기

인증서 오류는 Clash가 내보낸 것이 아니라 브라우저가 TLS 핸드셰이크 결과를 전달한 메시지입니다. Chrome, Edge, Firefox는 실패한 단계를 오류 문구에 이미 담고 있으므로, 이 문구를 그대로 기록한 뒤 아래 표와 대조해 어느 계층을 확인할지 정하는 편이 프록시를 껐다 켰다 하는 것보다 훨씬 효율적입니다.

Chrome / Edge 인증서 오류 문구와 우선 점검 방향
오류 문구의미우선 점검
NET::ERR_CERT_DATE_INVALID인증서가 유효 기간을 벗어남시스템 시간, 루트 인증서 유효 기간
NET::ERR_CERT_AUTHORITY_INVALID발급자가 로컬 신뢰 목록에 없음중간 인증서 누락, 로컬 가로채기 프로그램
NET::ERR_CERT_COMMON_NAME_INVALID인증서 도메인과 접속 도메인 불일치DNS 조회 결과, hosts 매핑
NET::ERR_CERT_REVOKED인증서가 폐기됨대상 사이트 측 문제
SSL_ERROR_BAD_CERT_DOMAIN(Firefox)도메인 불일치, 위와 동일DNS 조회와 SNI

Firefox는 자체 NSS 인증서 저장소를 사용하며 시스템 루트 인증서 저장소를 읽지 않습니다. 시스템에 기업 게이트웨이나 패킷 캡처 도구의 루트 인증서가 설치되어 있으면 Chrome에서는 페이지가 정상적으로 열리지만 Firefox에서는 여전히 SEC_ERROR_UNKNOWN_ISSUER가 발생할 수 있습니다. '같은 도메인인데 브라우저마다 결과가 다르다'면 프록시 경로가 아니라 인증서 저장소를 먼저 의심하세요.

명령줄을 쓰면 세부 정보를 더 빠르게 확인할 수 있습니다. 명령 두 개면 충분합니다:

curl -vI https://example.com --max-time 8
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

curl이 SSL certificate problem: unable to get local issuer certificate를 반환하면 인증서 체인이 불완전하거나 로컬에 해당 루트 인증서가 없다는 뜻이고, certificate has expired를 반환하면 시간이나 유효 기간 문제입니다. 이 두 가지 정보가 브라우저 경고창보다 근본 원인에 더 가깝습니다.

먼저 한 가지 동작으로 경계 긋기

클라이언트를 종료하고 시스템 프록시를 끈 뒤 같은 브라우저로 같은 도메인에 접속합니다. 프록시를 끈 뒤 정상으로 돌아오면 문제는 프록시 경로나 노드에 있고, 그래도 오류가 나면 로컬 인증서 저장소나 사이트 자체의 문제로 Clash와는 무관합니다. 이 단계만 거치면 이후에 시간을 헛되이 쓰지 않습니다.

2. 1단계: 시스템 시간과 인증서 유효 기간 확인

인증서 유효 기간은 notBefore와 notAfter 두 시점으로 정해지며, 시스템 시간이 이 범위를 벗어나면 핸드셰이크가 실패합니다. 노트북을 오래 절전 상태로 두거나, 듀얼 부팅을 반복하거나, 가상 머신 스냅샷을 되돌리면 시스템 시계가 몇 분에서 며칠까지 어긋날 수 있는데 사용자는 좀처럼 알아차리지 못합니다.

  • Windows: 설정 → 시간 및 언어 → 날짜 및 시간 → '자동으로 시간 설정'을 켜고 '지금 동기화'를 누릅니다. 명령줄에서는 w32tm /resync로 강제 동기화하고, w32tm /query /status로 현재 시간 원본을 확인합니다.
  • macOS: 시스템 설정 → 일반 → 날짜 및 시간 → '날짜 및 시간 자동 설정'을 켭니다. 터미널에서 sudo sntp -sS time.apple.com을 실행하면 수동으로 시간을 맞출 수 있습니다.
  • Linux: timedatectl status로 NTP 동기화 상태를 확인하고, sudo systemctl restart systemd-timesyncd로 시간 동기화 서비스를 재시작합니다.

시간이 정확한데도 ERR_CERT_DATE_INVALID가 계속 뜬다면 루트 인증서 자체를 확인해야 합니다. DST Root CA X3는 2021-09-30에 만료되었고, 당시 서버가 여전히 이 루트를 가리키는 교차 서명 체인을 내려보내면서 구형 Android 기기와 구버전 OpenSSL 클라이언트에서 연쇄 오류가 발생했습니다. 로컬 루트 인증서 목록을 확인하는 경로는 다음과 같습니다. Windows에서는 certlm.msc 실행 → 신뢰할 수 있는 루트 인증 기관 → 인증서, macOS에서는 '키체인 접근' → 시스템 → 인증서, Linux에서는 /etc/ssl/certs 디렉터리를 확인합니다.

판단 기준은 간단합니다. 같은 도메인을 시간이 정상인 다른 기기에서 열었을 때 문제가 없으면 로컬 시간이나 인증서 저장소 문제이며 노드와는 무관하므로 더 내려갈 필요가 없습니다.

3. 2단계: 루트 인증서 체인과 로컬 가로채기 프로그램 점검

중간 인증서 누락은 서버 설정 문제이지만 증상은 클라이언트 문제처럼 보이는 경우가 많습니다. 서버가 사이트 인증서만 내려보내고 중간 인증서를 보내지 않으면 브라우저는 캐시된 중간 인증서나 인증서의 AIA 확장으로 이를 채우려 시도합니다. 브라우저를 바꾸거나 캐시를 지운 뒤 이 보완이 실패하면 ERR_CERT_AUTHORITY_INVALID가 발생합니다.

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -c "BEGIN CERTIFICATE"

출력이 1이면 서버가 사이트 인증서만 반환해 체인이 불완전하다는 뜻이며, 정상이라면 2~3이 반환되어야 합니다. 이런 문제는 사이트 측에서 중간 인증서를 추가로 내려보내야만 해결되며 클라이언트 설정을 바꿔도 소용이 없습니다.

로컬 가로채기 프로그램은 두 번째로 흔한 원인입니다. 기업 게이트웨이, 웹 보호 기능이 있는 보안 프로그램, 패킷 캡처 도구(Charles, Fiddler, mitmproxy)는 로컬에 자체 서명 루트 인증서를 설치하고, 이 인증서로 접속하는 모든 사이트 인증서를 다시 서명합니다. 이 루트 인증서가 시스템 저장소에 있으면 Chrome과 Edge는 이를 신뢰해 모든 접속이 정상이지만, Firefox, Java 애플리케이션, 일부 Electron 클라이언트는 각자의 인증서 저장소를 사용하므로 오류가 발생합니다. 항목별 확인 위치는 다음과 같습니다.

  • Windows: certlm.msc → 신뢰할 수 있는 루트 인증 기관 → 인증서에서 '발급자' 열을 기준으로 정렬하고, 공용 CA가 아닌 항목(기업 도메인, 보안 프로그램 제조사 이름)을 찾습니다.
  • macOS: 키체인 접근 → 시스템 → 인증서에서 출처가 불분명한 자체 서명 루트 인증서가 있는지 확인합니다.
  • Firefox: 설정 → 개인 정보 및 보안 → 인증서 → 인증서 보기 → 인증 기관에서 시스템 목록과 하나씩 대조합니다.

검증을 건너뛰어 오류를 피하지 않기

curl의 --insecure나 브라우저의 ignore-certificate-errors 실행 옵션은 검증 기능을 아예 꺼버리는 방법입니다. 문제는 눈에 보이는 오류에서 보이지 않는 위험으로 바뀌고, 문제 해결의 단서도 함께 사라집니다.

한 가지 분명히 해둘 점이 있습니다. Clash와 mihomo 코어는 TCP/UDP 전달과 규칙 기반 분기만 담당하며 TLS 핸드셰이크에 관여하지 않고 HTTPS 트래픽도 복호화하지 않으므로, 프록시 경로 자체가 인증서를 바꾸는 일은 없습니다. MITM 도구를 따로 연결한 경우가 아니라면 인증서 오류를 코어 탓으로 돌리는 것은 처음부터 방향이 틀린 것입니다.

4. 3단계: 프록시 경로와 DNS, 분기 설정 점검

도메인이 잘못된 IP로 해석되는 것이 인증서 오류의 세 번째 원인입니다. 인증서의 CN과 SAN 필드는 도메인에 묶여 있으므로, 브라우저가 해당 도메인에 속하지 않는 인증서를 받으면 ERR_CERT_COMMON_NAME_INVALID가 발생합니다. 흔한 상황은 세 가지입니다. DNS가 오염되어 잘못된 주소를 반환하는 경우, hosts에 직접 적어둔 매핑이 만료된 경우, fake-ip 주소를 실제 주소로 착각해 다른 도구에 입력한 경우입니다.

dns:
  enable: true
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  nameserver:
    - https://223.5.5.5/dns-query

fake-ip 모드에서 mihomo는 도메인에 198.18.0.1/16 대역의 가상 주소를 할당합니다. 이 매핑은 클라이언트 내부에만 존재하고 연결이 맺어질 때 도메인으로 복원되므로 TLS 검증에 관여하지 않습니다. 실제로 문제가 되는 것은 198.18.x.x를 hosts나 프록시를 거치지 않는 도구에 적어 넣는 경우로, 도메인 정보가 사라지면 인증서가 맞지 않는 것이 당연합니다.

분기 규칙도 인증서 오류를 간접적으로 일으킬 수 있습니다. 어떤 도메인이 규칙에 따라 직접 연결로 판정되었는데 현재 네트워크가 그 도메인을 하이재킹하면 위조 인증서가 반환됩니다. 확인 방법은 Clash Verge의 '연결' 페이지에서 해당 연결을 찾아 적용된 규칙과 아웃바운드 노드를 확인한 뒤, 그 도메인을 프록시 노드로 전환해 다시 시도하는 것입니다. 전환 후 정상으로 돌아오면 문제는 인증서가 아니라 직접 연결 아웃바운드에 있습니다.

시스템 프록시가 적용되지 않는 경우는 따로 구분해야 합니다. 일부 애플리케이션은 시스템 프록시 설정을 읽지 않아 트래픽이 직접 연결로 나가며, 차단될 때는 인증서 오류가 아니라 연결 재설정이나 시간 초과가 발생합니다. 이 두 종류의 오류를 섞어서 확인하면 길을 돌아가게 됩니다. 포트가 일치하는지도 확인하세요. mixed-port는 보통 7890, Clash Verge 계열은 기본 7897이며, 브라우저 확장과 시스템 프록시가 같은 포트를 가리켜야 합니다.

TUN 모드는 네트워크 어댑터 계층에서 트래픽을 가로채므로 애플리케이션이 프록시를 지원하는지에 의존하지 않지만, 마찬가지로 TLS를 복호화하지 않으며 인증서 검증은 여전히 브라우저 쪽에서 이루어집니다. TUN 모드에서 인증서 오류가 발생하면 TUN 자체를 껐다 켰다 하기보다 DNS와 분기 규칙을 먼저 확인하세요.

5. 순서대로 실행하는 점검 목록

  1. 오류 문구 전체와 도메인, 발생 시각을 기록하고 지속적인 오류인지 간헐적인지 확인합니다.
  2. 클라이언트를 종료하고 시스템 프록시를 끈 뒤 같은 브라우저로 다시 시도해 프록시와 관련된 문제인지 판단합니다.
  3. 시스템 시간을 맞추고 브라우저를 재시작한 뒤 다시 접속합니다.
  4. openssl s_client -connect 도메인:443 -servername 도메인 -showcerts를 실행해 인증서 체인이 완전하고 유효 기간이 현재 시각을 포함하는지 확인합니다.
  5. 시스템 루트 인증서 목록을 확인해 기업 게이트웨이나 보안 프로그램이 설치한 가로채기 인증서를 걸러냅니다. Firefox는 별도로 한 번 더 확인해야 합니다.
  6. 클라이언트의 '연결' 페이지에서 해당 도메인에 적용된 규칙, 아웃바운드 노드, DNS 조회 결과를 확인합니다.
  7. 해당 도메인을 다른 노드나 다른 정책 그룹으로 전환해 오류 문구가 달라지는지 확인합니다.
  8. 다른 네트워크(예: 휴대폰 핫스팟)로 바꿔 교차 검증하고, 로컬 네트워크 문제인지 노드 측 문제인지 구분합니다.

목록에서 2번과 4번 단계의 효율이 가장 높습니다. 하나는 책임 범위를 나누고, 다른 하나는 인증서 체인의 실제 상태를 바로 보여줍니다. 대부분의 사례는 여기까지만 해도 원인을 찾을 수 있습니다.

6. 자주 발생하는 오해

  • 노드를 바꾸면 인증서 오류가 해결된다는 생각. 노드는 바이트 스트림만 전달하고 인증서는 대상 사이트가 제공합니다. 노드를 바꾼 뒤에도 같은 오류가 나온다면 노드는 원인에서 제외할 수 있습니다.
  • TUN 모드가 인증서 검증을 바꾼다는 생각. 아닙니다. TUN이 바꾸는 것은 트래픽을 가로채는 방식이며 TLS 검증은 여전히 브라우저 쪽에서 이루어집니다.
  • 구독을 업데이트하면 인증서 오류가 고쳐진다는 생각. 구독 내용은 노드 목록과 규칙에만 영향을 주며 시스템 인증서 저장소에 기록되지 않습니다.
  • 브라우저 캐시를 지우면 해결된다는 생각. 중간 인증서가 누락되었고 그동안 캐시로 이를 보완해 온 경우에만 드물게 효과가 있을 뿐, 안정적인 해결책은 아닙니다.
  • 클라이언트를 바꾸면 괜찮아진다는 생각. 주요 클라이언트는 mihomo 코어와 동일한 시스템 프록시 설정을 공유하므로 클라이언트를 바꿔도 TLS 검증 경로는 달라지지 않습니다.

순서를 고정하세요. 오류 문구 → 시스템 시간 → 루트 인증서 체인 → 프록시와 DNS → 노드. 앞의 두 단계만으로 대부분의 사례가 해결되며, 코어를 첫 번째 용의자로 삼으면 보통 시간만 더 쓰게 됩니다.