Skip to main content
EN

Home / Blog / Rules & Routing

Split tunnelling in practice — corporate intranet, local direct and overseas proxy together

Rules & Routing2026-07-171275 words3 min read
Split tunnelling in practice — corporate intranet, local direct and overseas proxy together

The classic remote-work situation: internal company systems have to go over the VPN, local sites should go direct, and overseas services need a proxy. All three at once, none stepping on the others.

Here is a configuration that does it.

First, three classes of traffic

Where each class should goCorporate intranetHR systems, GitLab, internal APIs, bastion hosts — must Local public internetlocal search, shopping, video, payments — direct, so it Overseas servicesdocumentation sites, developer services, foreign SaaS —
The three are identified differently: intranet by domain plus IP range, local by GEOIP, overseas by the catch-all

The hard part: intranet domains typically resolve to private IPs, but the names themselves look no different from public ones. Judging by GEOIP alone gets it wrong.

The full configuration

Step 1: define the policy groups

proxy-groups:
  - name: "🚀 Overseas"
    type: select
    proxies: ["♻️ Auto", "🇭🇰 Hong Kong", "🇯🇵 Japan", "🇺🇸 US"]

  - name: "♻️ Auto"
    type: url-test
    include-all: true
    exclude-filter: "(?i)remaining|expiry|website"
    url: "http://www.gstatic.com/generate_204"
    interval: 300
    tolerance: 50

  - name: "🏢 Intranet"
    type: select
    proxies: [DIRECT]        # intranet exits through the local interface (VPN already up)

  - name: "🇨🇳 Local direct"
    type: select
    proxies: [DIRECT, "🚀 Overseas"]   # switchable when needed

  - name: "🐟 Fallthrough"
    type: select
    proxies: ["🚀 Overseas", DIRECT]

Making intranet and local into policy groups rather than writing DIRECT inline means you can flip them in the interface while diagnosing, with no config edit.

Step 2: intranet rules (first in the list)

rules:
  # ===== 1. loopback and private ranges =====
  - IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
  - IP-CIDR,192.168.0.0/16,🏢 Intranet,no-resolve
  - IP-CIDR,10.0.0.0/8,🏢 Intranet,no-resolve
  - IP-CIDR,172.16.0.0/12,🏢 Intranet,no-resolve
  - IP-CIDR,100.64.0.0/10,DIRECT,no-resolve
  - IP-CIDR6,fc00::/7,DIRECT,no-resolve

  # ===== 2. corporate intranet domains =====
  - DOMAIN-SUFFIX,mycompany.com,🏢 Intranet
  - DOMAIN-SUFFIX,corp.internal,🏢 Intranet
  - DOMAIN-SUFFIX,intra,🏢 Intranet
  - DOMAIN-KEYWORD,gitlab-internal,🏢 Intranet
  - DOMAIN-SUFFIX,local,DIRECT
  - DOMAIN-SUFFIX,lan,DIRECT

Step 3: overseas rules

  # ===== 3. explicitly proxied =====
  - DOMAIN-SUFFIX,github.com,🚀 Overseas
  - DOMAIN-SUFFIX,githubusercontent.com,🚀 Overseas
  - DOMAIN-SUFFIX,docker.io,🚀 Overseas
  - DOMAIN-SUFFIX,npmjs.org,🚀 Overseas
  - DOMAIN-SUFFIX,pypi.org,🚀 Overseas
  - DOMAIN-SUFFIX,openai.com,🚀 Overseas
  - RULE-SET,proxy-list,🚀 Overseas

Step 4: local and catch-all

  # ===== 4. local direct =====
  - RULE-SET,direct-list,🇨🇳 Local direct
  - RULE-SET,cn-ip,🇨🇳 Local direct,no-resolve
  - GEOIP,CN,🇨🇳 Local direct

  # ===== 5. catch-all =====
  - MATCH,🐟 Fallthrough

DNS has to be routed too

This is the piece most often missed. Intranet names must be resolved by the company's DNS, otherwise they either fail to resolve or resolve to the wrong thing.

dns:
  enable: true
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  fake-ip-filter:
    - "*.lan"
    - "*.local"
    - "+.mycompany.com"        # intranet names skip fake-ip
    - "+.corp.internal"
    - "+.pool.ntp.org"

  default-nameserver: [223.5.5.5, 119.29.29.29]
  nameserver:
    - 223.5.5.5
    - 119.29.29.29

  # the key part: send specific names to specific resolvers
  nameserver-policy:
    "+.mycompany.com": "10.10.0.53"       # the corporate DNS server
    "+.corp.internal": "10.10.0.53"
    "geosite:cn": [223.5.5.5, 119.29.29.29]
    "geosite:geolocation-!cn": [https://1.1.1.1/dns-query]

  fallback:
    - https://1.1.1.1/dns-query
    - tls://8.8.4.4:853
  fallback-filter:
    geoip: true
    geoip-code: CN
The complete path for one intranet nameRequest for gitlab.mycompany.cthe application asksfake-ip-filter matchesno fake address is returnednameserver-policy matcheshanded to 10.10.0.53 to resolveReal internal IP returned10.20.30.40Rule matchesIP-CIDR 10.0.0.0Goes out directlythrough the VPN tunnel
All three links are required: skip fake-ip, use the internal resolver, and let the IP-range rule send it direct

nameserver-policy is the heart of this arrangement. Without it, intranet names are sent to a public resolver, and the answer is either NXDOMAIN or a carrier's landing page.

Common conflicts and what to do

Conflict 1: the corporate VPN and TUN fight over routes

Enterprise VPN clients (Cisco AnyConnect, FortiClient, SSL VPN products) generally rewrite the routing table. Clash Verge's TUN mode rewrites it too, and they clash easily.

What to do, in order of preferencePrefer the system proxy alone, no TUN — the smallest surface for conflictIf TUN is unavoidable, write the VPN's internal ranges into explicit DIRECT rulesAdjust the order: connect the VPN first, then enable TUNExclude the VPN adapter in the TUN config with exclude-interfaceIf the VPN forces a full-tunnel route — usually the two cannot coexist and you must pick one

The exclude-interface syntax:

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  exclude-interface: ["ppp0", "Cisco AnyConnect"]

Find the adapter name with ipconfig on Windows, ifconfig or ip link on Linux and macOS.

Conflict 2: your employer runs endpoint management software

EDR products may treat a virtual adapter as anomalous. In that case:

  • Do not enable TUN; use the system proxy only
  • Or simply handle personal matters on a personal device and keep the work machine clean

Conflict 3: an internal service went through the proxy

Symptom: the internal system times out, and the connections page shows an overseas node.

Investigation:

  1. Read the "Rule" column on the connections page to see what matched
  2. If it matched MATCH or GEOIP, your intranet rules did not fire
  3. Check the spelling of the intranet domain rules; check whether some broader rule above intercepted them
  4. Use prepend-rules to force the intranet rules to the very top

Conflict 4: a local site went through the proxy and tripped fraud detection

Symptom: your bank reports a login from an unusual location, or a payment platform demands a second factor.

Cause: those sites are not in direct-list, and their resolved IPs are not inside GEOIP CN (they may sit on an overseas CDN).

Fix: add rules by hand

prepend-rules:
  - DOMAIN-SUFFIX,icbc.com.cn,🇨🇳 Local direct
  - DOMAIN-SUFFIX,alipay.com,🇨🇳 Local direct
  - DOMAIN-SUFFIX,unionpay.com,🇨🇳 Local direct

Routing per device (LAN scenarios)

If Clash runs on a soft router or is shared with other devices, you can distinguish by source IP:

prepend-rules:
  # work laptop: only overseas services get proxied
  - AND,((SRC-IP-CIDR,192.168.1.100/32),(GEOIP,CN)),DIRECT
  # TV box: everything proxied
  - SRC-IP-CIDR,192.168.1.200/32,🚀 Overseas
  # IoT devices: everything direct
  - SRC-IP-CIDR,192.168.1.0/24,DIRECT

Matching is still top to bottom, so specific devices go above the subnet rule.

Verifying that it works

Four checks1Intranetopen the internal system in a browser; the connections page should show "Intranet → DIRECT"2Localvisit a local site; it should show "Local direct → DIRECT" with GEOIP,CN or a rule set in the Rule column3Overseasvisit github.com; it should show "Overseas → some node"4DNSrun nslookup gitlab.mycompany.com in a terminal; it should return the internal IP, not 198.18.x.x
All four correct means the split is genuinely in effect

In short

The keys to making all three coexist:

  • The intranet needs all three: domain rules, IP-range rules and nameserver-policy
  • Rule order: intranet → explicit overseas entries → local lists → GEOIP → MATCH
  • When the VPN and TUN conflict, prefer the system proxy alone
  • Make each class a policy group so you can flip it in the interface while diagnosing

Related: DNS configuration explained and the rule type reference.


Related docs

Routing individual programs with PROCESS-NAME rules
Rules & Routing Routing individual programs with PROCESS-NAME rules

The complete picture for per-process routing: enabling find-process-mode, finding the process name, PROCESS-NAME versus PROCESS-PATH, the performance cost, and why a rule you wrote does nothing.

2026-06-221224 words3 min read