먼저 프록시를 명시한 요청으로 문제 지점을 좁힌 다음 시스템 프록시나 DNS를 조정하세요. 클라이언트에 ‘연결됨’이라고 표시되어도 보통 커널이나 트래픽 가로채기 기능이 시작되었다는 뜻일 뿐, 브라우저 요청이 해당 커널을 통과하거나 현재 노드로 대상 웹사이트에 접속할 수 있다는 보장은 없습니다. ‘수신 포트 → 프록시 지정 요청 → 앱 프록시 → DNS → 규칙 및 노드 → TUN’ 순서로 확인하고, 한 번에 한 항목씩만 변경하세요.

1. 화면만 실행 중인 것이 아니라 커널이 수신 대기 중인지 확인

먼저 프로세스와 포트를 확인하세요. 아래 두 명령은 상태만 조회하며 설정을 변경하지 않습니다. ss에서 프로세스 정보를 보려면 관리자 권한이 필요할 수 있습니다. 권한이 없어도 수신 주소와 포트는 먼저 확인할 수 있습니다.

pgrep -af 'mihomo|clash'
sudo ss -lntp 'sport = :7890'
  • 수신 대기 결과가 없다면 커널 시작 실패, 설정 구문 오류 또는 실제 포트가 7890이 아닌 경우를 확인하세요.
  • 127.0.0.1:7890에서 수신 대기 중: 로컬 프로그램은 연결할 수 있지만, 같은 네트워크의 다른 기기에서는 이 주소를 사용할 수 없습니다.
  • 0.0.0.0:7890에서 수신 대기 중: IPv4 로컬 네트워크 인터페이스 전체에서 연결을 받습니다. LAN 접근 권한과 방화벽도 확인하세요.
  • 다른 프로세스가 포트를 사용 중이라면 먼저 어떤 프로세스인지 확인한 뒤 충돌하는 인스턴스만 종료하세요. 프록시 프로세스를 전부 한꺼번에 종료하지 마세요.

mixed-port는 HTTP와 SOCKS 요청을 모두 받습니다. 설정에서 port 또는 socks-port만 사용하는 경우에는 프로토콜에 맞는 포트를 사용해야 합니다. external-controller는 관리 인터페이스이므로 브라우저 프록시 포트로 사용할 수 없습니다.

# 이 디렉터리의 설정만 확인하며 상시 실행 인스턴스는 추가로 시작하지 않습니다
mihomo -t -d "$HOME/.config/mihomo"

서비스가 실행 중이라면 해당 설정을 현재 바이너리에서 구문 분석할 수 있다는 뜻일 뿐, 서비스가 그 설정을 실제로 사용한다는 보장은 없습니다. 직접 만든 systemd 사용자 서비스를 확인하려면 아래 명령을 실행하세요. 시스템 서비스라면 sudo systemctl을 사용하고 --user를 제거하세요.

systemctl --user cat mihomo.service
systemctl --user status mihomo.service --no-pager
journalctl --user -u mihomo.service -b -n 80 --no-pager

서비스를 찾을 수 없다는 메시지가 나오면 GUI 클라이언트의 커널 로그를 확인하세요. 문제 해결을 위해 같은 포트를 사용하는 서비스를 추가로 실행하지 마세요. address already in use, 설정 구문 분석 실패, 권한 부족, Provider 로드 오류를 중점적으로 찾아보세요.

2. curl에서 프록시를 직접 지정해 앱과 커널 문제 분리

먼저 HTTP 프록시 주소를 테스트하세요. --noproxy ""는 이번 요청의 우회 목록을 비워 환경 변수 NO_PROXY 때문에 의도치 않게 직접 연결되는 일을 막습니다. 결과를 비교하기 쉽도록 같은 HTTPS 주소를 사용합니다. 실제 문제를 확인할 때는 열리지 않는 주소도 같은 방식으로 테스트하세요.

curl --noproxy "" -x http://127.0.0.1:7890 \
  --connect-timeout 5 --max-time 20 \
  -v -o /dev/null https://example.com/
결과설명다음 단계
Connection refused로컬 TCP 연결 거부됨수신 포트와 주소 확인
407프록시 인증 필요설정의 인증 정보와 앱 자격 증명 확인
CONNECT 연결 후 시간 초과프록시 입구는 응답했지만 이후 연결이 완료되지 않음규칙, 노드, 대상 연결 로그 확인
certificate verify failedTLS 인증서 검증 실패시스템 시간, 인증서 저장소, 중간 프록시 확인
대상 서버의 HTTP 응답 수신해당 요청 경로에서 왕복 통신 가능브라우저와 시스템 프록시 설정 확인

HTTP 403 또는 429는 대상 서버나 중간 서비스가 응답했다는 뜻이지, 포트가 작동하지 않는다는 뜻은 아닙니다. 한 번 성공했다고 해서 모든 웹사이트, UDP, IPv6가 정상이라고 단정할 수도 없습니다.

프록시를 명시하지 않은 요청도 한 번 비교

curl --proxy "" --noproxy "*" \
  --connect-timeout 5 --max-time 20 \
  -I https://example.com/

이 명령은 curl의 프록시 지정을 해제하지만 시스템의 TUN, 정책 라우팅, 투명 프록시까지 우회하지는 않습니다. 실제 직접 연결을 기준으로 비교하려면 복구 가능한 로컬 환경에서 먼저 TUN을 끈 뒤 다시 테스트하세요. 프록시를 지정한 요청은 성공하고 브라우저만 실패한다면 DNS를 바로 바꾸지 말고 다음 절을 확인하세요.

3. 시스템 프록시·브라우저 설정·터미널 환경 변수 확인

Linux 데스크톱 프록시, 터미널 환경 변수, 브라우저 자체 설정은 각각 다른 연결 경로입니다. 하나를 켰다고 나머지도 자동으로 적용되는 것은 아닙니다. GNOME 데스크톱의 경우 ‘설정’ → ‘네트워크’ → ‘네트워크 프록시’에서 ‘수동’을 선택하고 HTTP와 HTTPS 프록시 호스트를 127.0.0.1, 포트를 7890으로 지정하세요. 배포판마다 메뉴 이름은 조금 다를 수 있습니다.

HTTPS 항목은 ‘HTTPS 요청이 어떤 프록시를 사용할지’를 지정하며, 프록시 프로토콜을 HTTPS로 설정하라는 뜻은 아닙니다. 로컬 mixed-port를 사용할 때 터미널의 https_proxy에는 보통 http://127.0.0.1:7890을 지정합니다.

# 현재 셸의 프록시 변수 확인: 출력에 인증 정보가 포함될 수 있으므로 공개하지 마세요
env | grep -iE '^(http|https|all|no)_proxy='

# 이번 curl 요청에만 프록시 설정
http_proxy=http://127.0.0.1:7890 \
https_proxy=http://127.0.0.1:7890 \
curl --noproxy "" --max-time 20 -I https://example.com/

우회 목록과 중복 트래픽 가로채기 확인

  • NO_PROXY=*로 설정하면 이 변수를 지원하는 프로그램은 프록시를 우회합니다. 테스트할 때는 값을 비운 다음 로컬 및 내부 네트워크 예외를 하나씩 다시 추가하세요.
  • Firefox에서는 ‘설정’ → ‘일반’ → ‘네트워크 설정’ → ‘설정’에서 시스템 프록시와 수동 프록시 중 어느 설정을 사용하는지 확인하세요. 확장 기능이 프록시 설정을 대신 관리할 수도 있습니다.
  • 기존 셸, 이미 실행 중인 IDE, 백그라운드 서비스는 새 터미널의 환경 변수를 자동으로 물려받지 않습니다. 해당 프로세스의 환경에서 다시 확인해야 합니다.
  • 컨테이너 안의 127.0.0.1은 호스트가 아니라 컨테이너 자신을 가리킵니다. 호스트 포트가 정상이라고 해서 컨테이너에서도 연결된다는 뜻은 아닙니다.

이전 환경 변수가 영향을 주는지 의심된다면 아래 명령으로 한 번만 깨끗한 상태에서 테스트하세요. 이 명령은 이번 자식 프로세스에만 적용되며 셸 설정 파일의 영구 설정은 삭제하지 않습니다.

env -u http_proxy -u https_proxy -u all_proxy -u no_proxy \
  -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY -u NO_PROXY \
  curl --noproxy "" -x http://127.0.0.1:7890 \
  --max-time 20 -I https://example.com/

4. 시스템 DNS, 커널 DNS, fake-ip 구분

도메인 조회 실패가 반드시 시스템 DNS 문제인 것은 아닙니다. HTTP CONNECT는 대상 호스트 이름을 프록시에 전달할 수 있고, SOCKS는 어느 쪽에서 이름을 조회할지 선택할 수 있습니다. 아래 두 명령을 비교하세요. 첫 번째는 curl이 로컬에서 조회하고, 두 번째는 도메인 조회를 SOCKS 프록시에 맡깁니다.

curl --noproxy "" --socks5 127.0.0.1:7890 \
  --max-time 20 -I https://example.com/

curl --noproxy "" --socks5-hostname 127.0.0.1:7890 \
  --max-time 20 -I https://example.com/

로컬 조회 경로는 실패하고 프록시 측 조회는 성공한다면 시스템 리졸버를 먼저 확인할 만합니다. 다만 두 요청이 서로 다른 IP와 규칙을 사용할 수 있으므로 이것만으로 원인을 단정할 수는 없습니다. 시스템에서 실제로 사용하는 DNS 설정을 계속 확인하세요.

getent ahosts example.com
ls -l /etc/resolv.conf
cat /etc/resolv.conf

# systemd-resolved를 사용하는 시스템에서만 해당
resolvectl status
resolvectl query example.com

/etc/resolv.conf가 systemd-resolved 또는 NetworkManager가 관리하는 파일을 가리킨다면 직접 덮어써도 재연결 후 설정이 사라질 수 있습니다. DHCP가 업데이트하는 도중 수동으로 값을 쓰지 말고, 해당 네트워크 관리자에서 DNS를 변경하세요.

mihomo의 DNS 수신 주소와 fake-ip 경로 확인

실제 실행 설정에 dns.listen: 127.0.0.1:1053이 있다고 가정하면, dig가 설치된 터미널에서 커널 DNS에 직접 질의할 수 있습니다. 수신 포트가 다르면 명령의 포트도 맞춰 변경하세요.

dig @127.0.0.1 -p 1053 example.com A
dig @127.0.0.1 -p 1053 example.com AAAA

enhanced-mode: fake-ip에서는 도메인 조회 결과가 설정 범위 안의 가상 IP일 수 있으며, 흔히 198.18.0.1/16 범위를 사용합니다. 이는 공인 대상 서버의 주소가 아닙니다. 이후 연결은 도메인 매핑을 보유한 프록시 커널로 돌아가야 합니다. 앱이 fake-ip를 받았지만 트래픽을 가로채지 않는 경로로 직접 연결하면 계속 시간 초과가 발생할 수 있습니다.

상위 DNS도 도메인 조회나 프록시 경로에 의존할 수 있습니다. default-nameserver, nameserver, proxy-server-nameserver의 용도와 현재 설정을 확인해 노드 도메인 조회가 아직 준비되지 않은 프록시 경로에 의존하지 않도록 하세요. 필드 설명은 설정 참조에서 확인할 수 있습니다. 모든 상위 DNS를 한꺼번에 바꾼 뒤 무엇이 적용됐는지 추측하지 마세요.

5. 규칙 매칭과 노드 상태 확인: 지연 시간만 보지 마세요

클라이언트의 연결 기록에서 테스트 도메인을 찾아 최종 아웃바운드가 DIRECT, 특정 프록시 노드, 거부 중 무엇인지 확인하세요. 규칙은 위에서 아래 순서로 매칭되며 마지막의 MATCH는 기본 규칙입니다. 앞쪽의 도메인 또는 IP 규칙이 이미 매칭됐다면 기본 그룹을 바꿔도 해당 요청에는 영향이 없습니다.

  1. 테스트 시간과 대상 도메인을 기록한 다음 프록시를 지정해 curl 요청을 다시 실행하세요.
  2. 연결 기록이나 로그에서 해당 도메인을 찾아 매칭된 규칙과 정책 그룹을 확인하세요.
  3. 정책 그룹에서 현재 선택된 항목을 확인하세요. 특히 중첩 그룹이 최종적으로 연결하는 실제 노드를 확인해야 합니다.
  4. 사용 가능한 것으로 확인된 다른 노드를 임시로 선택하고 대상 주소, 요청 방식, 모드는 그대로 둔 채 결과를 비교하세요.

지연 시간 테스트에 성공했다는 것은 해당 시점에 설정된 테스트 URL에 연결할 수 있었다는 뜻일 뿐, 모든 사이트에 접속할 수 있다는 뜻은 아닙니다. 노드는 특정 대상, 프로토콜, 주소 계열에서만 실패할 수 있습니다. 구독 업데이트 성공도 설정을 가져왔다는 뜻일 뿐, 노드 인증 정보, 사용량 한도, 서버 포트가 정상이라는 보장은 아닙니다.

잠시 로그 수준을 높인 뒤 원래대로 복구

필요하면 실제 설정의 log-level을 일시적으로 debug로 바꾸고 한 번 재현한 뒤 info로 되돌리세요. GUI 클라이언트에서는 제공되는 설정 오버라이드나 로그 설정을 사용해야 생성된 실행 설정이 다음 업데이트에서 덮어써지지 않습니다. 로그를 공유하기 전에 구독 주소, 인증 정보, 개인용 접근 도메인을 삭제하세요.

# 직접 만든 사용자 서비스: 설정을 수정한 뒤 먼저 테스트하고, 성공하면 재시작
mihomo -t -d "$HOME/.config/mihomo" && \
  systemctl --user restart mihomo.service

journalctl --user -u mihomo.service --since "5 minutes ago" --no-pager

인증서 오류가 발생하면 먼저 timedatectl status를 실행해 시간과 동기화 상태를 확인한 다음 인증서 저장소와 노드 도메인을 살펴보세요. 인증서 검증을 끄는 것은 일반적인 해결 방법이 아닙니다. 주소 오류나 중간 연결 문제를 감출 수 있습니다.

6. 프록시 지정 요청은 성공하지만 TUN은 실패할 때: 권한·라우팅·IPv6 확인

curl -x는 정상인데 TUN을 켜면 브라우저가 실패하고 포트와 노드가 정상이라는 근거가 있다면, 투명 트래픽 가로채기를 중점적으로 확인하세요. TUN은 가상 네트워크 인터페이스, 적절한 권한, 라우팅에 의존합니다. 설정에 tun.enable: true가 있다고 해서 이 조건들이 충족됐다는 뜻은 아닙니다.

ls -l /dev/net/tun
ip -br link
ip rule show
ip route show table all
ip -6 route show table all
  • /dev/net/tun이 없다면 시스템에서 TUN을 지원하고 활성화했는지 확인하세요. 컨테이너나 제한된 가상화 환경에서는 호스트의 권한 부여도 필요합니다.
  • 인터페이스 생성 또는 라우팅 변경 실패 메시지가 로그에 있다면 클라이언트에서 제공하는 서비스 모드를 사용하거나 직접 만든 서비스의 권한을 확인하세요. 실행 파일 권한을 무작정 확대하지 마세요.
  • 다른 VPN도 함께 실행 중이라면 양쪽의 정책 라우팅, 기본 경로, DNS 가로채기 설정을 비교하세요. 테스트할 때는 트래픽을 가로채는 경로를 한 번에 하나만 유지하세요.
  • IPv4는 정상이고 IPv6만 실패한다면 IPv6 기본 경로, DNS AAAA 응답, 현재 커널의 IPv6 처리 설정을 각각 확인하세요.
# TUN을 끈 뒤 호스트의 기본 IPv4 / IPv6 연결 상태 비교
curl --proxy "" --noproxy "*" -4 --max-time 15 -I https://example.com/
curl --proxy "" --noproxy "*" -6 --max-time 15 -I https://example.com/

여기서 -4와 -6은 직접 연결의 기준 상태를 확인하는 옵션입니다. 로컬 HTTP 프록시에 같은 옵션을 적용해도 원격 노드가 대상에 접속할 때 사용하는 주소 계열까지 확실히 제어할 수는 없습니다. 네트워크에 IPv6 출구가 없다면 IPv6 테스트 실패만으로 노드에 문제가 있다고 볼 수 없습니다.

7. 필요한 부분만 변경해 복구하고 재현 기록 보관

점검을 마친 뒤 같은 대상을 사용해 터미널, 브라우저, TUN의 세 경로를 다시 확인하세요. 시스템 트레이 아이콘이나 속도 테스트 한 번만으로 복구를 판단하지 마세요. 같은 문제가 다시 발생했을 때 바로 비교할 수 있도록 다음 내용을 기록해 두는 것이 좋습니다.

  1. 실행 인스턴스: 커널 버전, 실제 설정 경로, 수신 주소, mixed-port를 기록하세요. mihomo -v로 버전을 확인할 수 있습니다.
  2. 앱 연결 경로: 브라우저가 시스템 프록시와 수동 프록시 중 무엇을 사용하는지, 터미널에 프록시 변수가 남아 있는지 기록하세요.
  3. DNS 경로: 시스템 DNS, 커널 DNS 수신 포트, fake-ip 사용 여부, 브라우저의 보안 DNS 활성화 여부를 기록하세요.
  4. 아웃바운드 결과: 테스트 도메인에 매칭된 규칙, 실제 노드, 오류 발생 시간을 기록하세요. 구독 키는 기록하지 마세요.
  5. 복구 설정: debug 로그를 평소 수준으로 되돌리고, 임시 전체 모드와 더 이상 필요하지 않은 테스트 변경 사항을 취소하세요.

문제 구분에 가장 유용한 기준은 다음과 같습니다. 프록시를 지정한 요청도 실패하면 커널과 아웃바운드를 먼저 확인하고, 해당 요청은 성공하지만 브라우저가 실패하면 앱 연결 경로를 먼저 확인하세요. TUN에서만 실패하면 투명 트래픽 가로채기를, 로컬 DNS 조회에서만 실패하면 시스템 DNS 경로를 점검하세요. 기본 설정을 다시 구성해야 한다면 빠른 시작 가이드를 따라 최소 연결 경로를 만든 뒤 규칙, Provider, TUN을 하나씩 복원하세요.