Shadowrocket REJECT 규칙: 차단 원리와 적용 범위

REJECT는 규칙에서 쓸 수 있는 세 가지 아웃바운드 정책 가운데 하나로, 매칭된 연결은 로컬에서 거부되거나 폐기되며 요청은 어떤 서버로도 전달되지 않습니다. 이 글에서는 REJECT의 매칭 순서와 실제 적용 범위, 그리고 HTTPS·앱 내부 요청·도메인 프론팅에서의 한계를 정리합니다.

이 글 한눈에 보기

REJECT는 연결 하나를 통과시킬지 여부만 결정할 뿐, 전송 내용을 해석하지 않습니다. 이 글을 읽고 나면 REJECT와 PROXY, DIRECT가 규칙 목록에서 어떤 위치 관계를 갖는지 구분할 수 있고, 같은 도메인 아래의 프로모션 API와 IP를 하드코딩한 앱 내부 요청을 왜 막지 못하는지, 그리고 '규칙을 썼는데 적용되지 않는' 상황을 어떤 순서로 점검해야 하는지 알 수 있습니다. 이미 서비스 제공업체에서 구독을 받아 직접 규칙을 정리하고 있는 사용자에게 맞는 내용입니다.

규칙에서 REJECT란: 하나의 아웃바운드 정책

Shadowrocket의 규칙 한 줄은 유형,값,정책 세 부분으로 이루어지고, 정책 자리에는 PROXY(현재 선택된 서버로 아웃바운드), DIRECT(로컬 직접 연결, 프록시를 거치지 않음), REJECT(이 연결을 로컬에서 거부 또는 폐기) 세 가지 값만 들어갑니다. REJECT는 Settings에 있는 일괄 스위치가 아니라 특정 규칙 하나의 동작이며, 그 규칙에 매칭된 연결만 차단됩니다.

최소한으로 동작하는 차단 규칙은 다음과 같습니다:

DOMAIN-SUFFIX,ads.example.com,REJECT
DOMAIN-KEYWORD,metrics,REJECT
IP-CIDR,203.0.113.0/24,REJECT,no-resolve
FINAL,PROXY

규칙은 현재 Config 파일에 기록되며(Home → 현재 구성 → 편집), Home의 규칙 목록에서 직접 추가하거나 삭제할 수도 있습니다. 수정한 뒤에는 해당 Config를 다시 선택해야 매칭에 참여합니다. 구독을 업데이트하면 구성 파일을 다시 가져오므로 로컬에서 직접 고친 규칙이 덮어써질 수 있으니, 업데이트 후에는 한 번 확인하는 편이 좋습니다.

3가지
아웃바운드 정책: PROXY / DIRECT / REJECT
위에서 아래로
규칙 매칭 순서: 첫 매칭이 곧바로 적용
2곳
규칙 진입점: Config 파일과 Home 규칙 목록
연결 계층
REJECT 적용 계층: 전송 내용을 해석하지 않음

매칭 순서: REJECT는 언제 차례를 얻는가

새 연결마다 규칙 매칭은 한 번만 이루어집니다. 목록 첫 줄부터 한 줄씩 비교하고, 처음으로 매칭된 규칙이 그 연결의 방향을 결정하며 뒤의 규칙은 참여하지 않습니다. REJECT가 적용될 수 있는지는 앞쪽에 더 넓은 규칙이 있어 이 연결을 먼저 가져가는지에 달려 있습니다.

앱이 요청 생성연결이 터널로 진입규칙을 위에서 아래로 매칭첫 매칭 적용연결이 로컬에서 거부됨

순서 문제는 규칙의 넓고 좁음에서 비롯됩니다. DOMAIN-KEYWORD,ads,REJECTDOMAIN-SUFFIX,ads.example.com,PROXY보다 앞에 있으면 ads.example.com은 keyword 규칙에 먼저 걸립니다. 반대로 배치하면 좁은 규칙이 먼저 매칭되어 뒤의 keyword 규칙은 이 연결에 적용될 기회를 얻지 못합니다. 좁은 규칙을 앞에, 넓은 규칙을 뒤에 두는 것이 규칙 목록의 기본 배열 방식입니다.

DOMAIN,api.example.com,DIRECT
DOMAIN-SUFFIX,example.com,PROXY
DOMAIN-KEYWORD,metrics,REJECT
IP-CIDR,198.51.100.0/24,REJECT,no-resolve
GEOIP,CN,DIRECT
FINAL,PROXY

이 여섯 줄은 바로 쓸 수 있는 골격입니다: 정확한 도메인 → 도메인 접미사 → 키워드 → IP 대역 → 지역 정보 → 최종 처리, 구체적인 규칙일수록 앞에 둡니다.

FINAL은 앞에서 매칭되지 않은 모든 연결을 처리하는 최종 규칙으로 반드시 마지막 줄에 있어야 합니다. 중간에 두면 그 뒤에 오는 모든 규칙이 무효가 됩니다. IP 계열 규칙에는 세부 사항이 두 가지 더 있습니다. no-resolve가 붙으면 이 규칙은 DNS 조회를 트리거하지 않고 대상이 IP 리터럴일 때만 매칭됩니다. no-resolve가 없는 IP-CIDR은 도메인을 먼저 IP로 해석한 뒤 비교하므로, 해석 결과에 따라 될 때와 안 될 때가 갈릴 수 있습니다.

차단 경계: HTTPS, 앱 내부 요청, 도메인 프론팅

REJECT는 연결이 맺어지는 단계에서 동작합니다. 연결을 로컬에서 거부하거나 폐기할 뿐, TLS 핸드셰이크 이후의 데이터 교환에는 관여하지 않습니다. 여기서 능력의 상한이 정해집니다. REJECT가 다루는 것은 '이 연결이 통과할 수 있는가'이지 '페이지에 무엇이 표시되는가'가 아닙니다.

경계 1: HTTPS는 연결 계층까지만 차단

https://ads.example.com/x.js 같은 요청에서 REJECT가 막는 것은 ads.example.com:443으로 가는 연결이며, 스크립트는 당연히 받아지지 않습니다. 하지만 광고 콘텐츠가 본문과 같은 도메인에서 온다면(예: example.com 아래의 특정 API) REJECT는 힘을 쓰지 못합니다. 도메인을 통째로 막으면 본문도 함께 열리지 않기 때문입니다. 규칙에는 URL 경로를 매칭하는 키워드가 없고, 클라이언트는 HTTPS 요청의 경로를 볼 수 없습니다.

경계 2: 앱 내부 요청과 하드코딩된 IP

많은 앱이 리포팅 주소를 IP 리터럴로 박아 넣거나, API에서 도메인을 동적으로 내려받습니다. 전자는 도메인 규칙으로 매칭되지 않아 IP-CIDR로 받쳐야 하고, 후자는 실제 도메인이 나타난 뒤에 규칙을 추가해야 합니다. IP로 차단할 때의 대가는 오탐이 나기 쉽다는 점입니다. 같은 대역에 정상 서비스가 함께 있는 경우가 많고, CDN IP를 공유하는 환경에서는 특히 그렇습니다. 도메인 규칙과 IP 규칙은 서로를 보완하는 두 진입점이며, 각각 아래 두 가지 요청 방식에 대응합니다:

DOMAIN-SUFFIX,ads.example.com,REJECT
IP-CIDR,203.0.113.0/24,REJECT,no-resolve

또 하나의 세부 사항은 QUIC입니다. UDP 443으로 가는 연결도 규칙 매칭을 거치지만, no-resolve가 붙은 IP-CIDR을 쓰고 있고 앱이 도메인 형태로 연결을 시작했다면 이 규칙은 조회를 트리거하지 않으므로 매칭되지 않습니다.

경계 3: 도메인 프론팅

도메인 프론팅(domain fronting)은 TLS 핸드셰이크의 SNI와 요청이 실제로 향하는 Host를 서로 다르게 만드는 방식이며, SNI는 보통 '깨끗한' 도메인입니다. Shadowrocket은 연결의 대상 도메인, 즉 SNI를 기준으로 규칙을 매칭하고, DOMAIN-SUFFIX가 막는 것은 SNI에 적힌 도메인입니다. SNI 기준으로 막으면 같은 진입점을 쓰는 정상 서비스까지 영향을 받고, Host 기준 차단은 클라이언트 규칙이 처리할 수 있는 범위 밖입니다.

결론: REJECT는 목록 기반 연결 차단이며 콘텐츠 필터가 아닙니다

도메인과 IP로 매칭하고 연결 계층에서 동작한다는 것은 독립된 도메인과 독립된 IP로 가는 요청은 막을 수 있다는 뜻입니다. 같은 도메인 아래의 프로모션 API, URL 경로 단위의 콘텐츠, SNI와 Host가 어긋난 트래픽은 모두 처리 범위 밖입니다. 이 경계를 기준으로 기대치를 잡으면 규칙을 고쳐 가며 시행착오를 겪는 일을 크게 줄일 수 있습니다.

규칙을 썼는데 적용되지 않을 때: 이 순서로 점검

규칙이 적용되지 않는 이유는 대부분 '현재 적용 중인 구성'과 '매칭 순서' 두 가지에 있습니다. 아래 순서대로 하나씩 확인하는 편이 규칙을 반복해서 다시 쓰는 것보다 빠릅니다.

  1. 현재 적용 중인 Config 확인

    Home 상단에 표시된 구성 이름을 확인합니다. 규칙은 A 구성에 썼는데 현재 선택된 것이 B라면 그 규칙은 매칭에 참여하지 않습니다.

  2. 목록에서 규칙 위치 확인

    더 앞에 있는 넓은 규칙이 먼저 매칭되지 않는지, 특히 DOMAIN-KEYWORD가 DOMAIN-SUFFIX보다 앞에 있는 경우를 확인합니다.

  3. 연결이 도메인으로 시작됐는지 IP로 시작됐는지 확인

    도메인 규칙은 IP 직접 연결에 매칭되지 않고, no-resolve가 붙은 IP-CIDR도 도메인 연결에 대해 조회를 트리거하지 않습니다.

  4. Global Routing 상태 확인

    Proxy 또는 Direct에 머무는 동안에는 규칙 목록이 라우팅 판단에 참여하지 않습니다. Config로 돌아와야 규칙을 한 줄씩 매칭합니다.

  5. 구독 업데이트 후 규칙 재확인

    구독을 업데이트하면 구성을 다시 가져오므로 로컬에서 직접 고친 규칙이 덮어써질 수 있습니다. 업데이트 후 Home으로 돌아가 규칙이 남아 있는지 확인합니다.

주의

REJECT는 Shadowrocket 터널을 거치는 연결에만 적용되고, 전송 내용도 해석하지 않습니다. 규칙이 바꿀 수 있는 것은 연결의 방향이지 앱 자체의 동작이 아닙니다.

REJECT에 관한 자주 묻는 질문 5가지

REJECT를 추가했더니 같은 도메인의 정상 페이지도 열리지 않습니다.

그 도메인이 본문과 차단 대상 콘텐츠를 함께 제공하고 있다는 뜻입니다. REJECT는 도메인 단위로 통째로 막기 때문에 일부만 막을 수 없습니다. 규칙을 특정 하위 도메인으로 좁히거나, DOMAIN으로 호스트 이름 하나만 정확히 매칭하도록 바꾸세요.

REJECT와 DIRECT는 결국 무엇이 다른가요?

DIRECT는 통과입니다. 연결은 정상적으로 맺어지고 프록시를 거치지 않을 뿐입니다. REJECT는 로컬에서 거부하거나 폐기하므로 연결 자체가 맺어지지 않습니다. '연결은 되지만 프록시를 거치지 않게' 하려면 DIRECT, '연결이 안 되게' 하려면 REJECT를 씁니다.

규칙을 Home에서 썼는데 구독을 업데이트하니 사라졌습니다.

구독 업데이트는 구성 파일을 다시 가져오므로 로컬에서 직접 고친 규칙이 덮어써질 수 있습니다. 오래 유지해야 하는 규칙은 업데이트 후 Home에서 한 번 확인하고, 필요하면 다시 추가하세요.

IP로 한 대역을 통째로 막았더니 다른 서비스도 함께 죽었습니다.

IP-CIDR은 대역 전체를 막기 때문에 CDN IP를 공유하는 곳에는 정상 서비스도 함께 있습니다. 도메인 규칙을 우선 사용하고, 정말 IP로 처리해야 한다면 대역을 더 잘게 나누고 no-resolve를 붙여 불필요한 조회를 피하세요.

REJECT된 연결이 Data 페이지에서 트래픽으로 보이지 않는 이유는 무엇인가요?

연결이 맺어지는 단계에서 끝나 실제 전송량이 없으므로 서버별·앱별 통계 수치가 늘지 않는 것이 당연합니다. 규칙이 매칭됐는지는 Home의 현재 구성과 규칙 순서를 기준으로 판단하세요.

App Store 정품 확인