Version status and lineage of the two cores
The original Clash (Dreamacro/clash) was archived at the end of 2023; its last stable release was v1.18.0, with no commits since. Around the same time there was also a closed-source Clash Premium core that offered TUN, rule-provider, and script support, but it was never open source and stopped updating alongside the original. Clash.Meta is a community rewrite of the original core, renamed mihomo in 2024 and maintained at MetaCubeX/mihomo, where development continues.
At the configuration file level the two are forward compatible: port, socks-port, mixed-port, allow-lan, mode, log-level, external-controller, proxies, proxy-groups, and rules are all recognized by both. The differences come down to four areas: inbound protocols, rule types, DNS resolution, and TUN implementation. Each is compared below, along with the fields you need to rewrite when migrating.
| Capability | Original Clash v1.18.0 | mihomo |
|---|---|---|
| Inbound listeners | Three top-level switches: port, socks-port, redir-port | Top-level switches + a listeners section declaring multiple inbounds |
| Proxy protocols | ss、vmess、trojan、snell、socks5、http | All of the above + vless, hysteria2, tuic, wireguard, ssh, anytls |
| Rule types | Domain, IP, port, GEOIP, MATCH | All of the above + GEOSITE, IP-ASN, regex, logical rules, SUB-RULE |
| Rule sets | Not supported; rules must be hardcoded in the config file | rule-providers, with support for the mrs binary format |
| DNS | nameserver + fallback + fallback-filter | nameserver-policy, split DNS, DoQ, fake-ip whitelist mode |
| TUN | Not supported | Three stacks: system / gvisor / mixed |
| Maintenance status | Archived in late 2023 | Actively maintained |
Protocol support: which proxy types only mihomo accepts
The original Clash only accepts six types in the proxies section: ss, vmess, trojan, snell, socks5, and http. Encountering any other type makes the core report unsupported proxy type during parsing and exit — it will not skip the node and keep running. This is the most common first error after switching cores.
mihomo adds vless (including XTLS Vision and REALITY), hysteria2, tuic v5, wireguard, ssh, and anytls to that list, and extends Shadowsocks ciphers to the 2022 series: 2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm, 2022-blake3-chacha20-poly1305. The original core cannot parse a single one of the three node field combinations below.
proxies:
- name: vless-vision
type: vless
server: edge.example.com
port: 443
uuid: 8f2c1d40-3a7e-4b91-9c2d-5e6f7a8b9c0d
network: tcp
tls: true
udp: true
flow: xtls-rprx-vision
servername: www.example.com
client-fingerprint: chrome
reality-opts:
public-key: uM7Kd2QpX1sVbN4tRzY8wLcE3aHfJgOiPqSvTnBm5kU
short-id: 6ba85179e30d4fc2
- name: hy2-edge
type: hysteria2
server: edge.example.com
port: 8443
password: 9f2c7d1a4b6e
sni: www.example.com
skip-cert-verify: false
up: "30 Mbps"
down: "200 Mbps"
- name: tuic-edge
type: tuic
server: edge.example.com
port: 10443
uuid: 8f2c1d40-3a7e-4b91-9c2d-5e6f7a8b9c0d
password: 9f2c7d1a4b6e
congestion-controller: bbr
udp-relay-mode: native
alpn: [h3]
To tell whether a subscription can still be fed to the original core, check just two things: the type values used in proxies, and the cipher on ss nodes. If every type is one of ss, vmess, trojan, or snell and the ciphers are older values such as aes-128-gcm, both cores will run it; as soon as reality-opts, congestion-controller, or a cipher starting with 2022-blake3- appears, you need mihomo.
Rule syntax and match order
The matching model is the same on both sides: rules are evaluated top to bottom, the first match wins, and MATCH is the catch-all. The differences lie in available types, matching cost, and how rule sets are loaded.
| Rule types | Original Clash | mihomo | Notes |
|---|---|---|---|
| DOMAIN / DOMAIN-SUFFIX / DOMAIN-KEYWORD | Supported | Supported | Uses a domain index; a single rule costs almost nothing |
| DOMAIN-REGEX / DOMAIN-WILDCARD | Not supported | Supported | Regex is evaluated per rule; placing these at the top slows down the first packet |
| IP-CIDR / IP-CIDR6 / SRC-IP-CIDR | Supported | Supported | Domain connections trigger resolution; add no-resolve |
| GEOIP | Supported | Supported | Requires the GeoIP data file |
| GEOSITE | Not supported | Supported | Requires the geosite data file |
| IP-ASN / IP-SUFFIX | Not supported | Supported | Routes by ASN or IP suffix range |
| PROCESS-NAME | Closed-source Premium only | Supported | Routes by process name on desktop |
| PROCESS-PATH / PROCESS-NAME-REGEX | Not supported | Supported | Matches by executable path |
| RULE-SET | Closed-source Premium only | Supported | Can add format: mrs |
| SUB-RULE / AND / OR / NOT | Not supported | Supported | Combines multiple conditions; watch the parentheses and commas |
| IN-TYPE / IN-USER / IN-PORT / NETWORK | Not supported | Supported | Matches by inbound source and transport protocol |
| MATCH | Supported | Supported | Must be the last rule |
Rule set differences are easier to overlook than rule types. The original Clash has no rule-providers support, so rules must be hardcoded in config.yaml; mihomo's rule-providers accept behavior values of domain, ipcidr, and classical, and format values of yaml, text, and mrs, where mrs is a binary format that only works with domain or ipcidr. mrs builds an index by domain prefix at load time instead of parsing the whole YAML into objects, and the gap is most obvious once rule counts reach the tens of thousands.
rule-providers:
reject-list:
type: http
behavior: domain
format: mrs
url: "https://rules.example.com/reject.mrs"
path: ./ruleset/reject.mrs
interval: 86400
rules:
- DOMAIN-SUFFIX,example.org,DIRECT
- GEOSITE,category-ads-all,REJECT
- RULE-SET,reject-list,REJECT
- IP-CIDR,198.18.0.0/16,DIRECT,no-resolve
- AND,((NETWORK,udp),(DST-PORT,443)),Proxy
- MATCH,Proxy
There are three ordering mistakes that are easiest to make when writing the rules section:
- Specific domains must come before GEOSITE. GEOSITE matches broadly, so if it is placed ahead of DOMAIN-SUFFIX,example.org, the later rule will never run.
- When IP-CIDR comes before domain rules, a domain connection must be resolved to an IP before it can be compared — that adds an extra DNS lookup and hands the result to the upstream resolver. Adding no-resolve lets domain connections skip the rule entirely.
- The policy group MATCH points to must contain at least one usable node. When a rule set update leaves some domains uncovered, all traffic falls through to MATCH, and an empty group means the whole machine loses connectivity.
GEOSITE and GEOIP depend on data files. The original Clash downloads Country.mmdb on first start; mihomo adds geodata-mode, geox-url, geo-auto-update, and geo-update-interval, so you can point the data source at your own mirror and refresh it on an hourly schedule instead of replacing files by hand.
DNS and fake-ip implementation differences
The dns section in the original Clash only has nameserver, fallback, fallback-filter, enhanced-mode, fake-ip-range, fake-ip-filter, and hosts. Every domain goes through the same resolution path, and fallback-filter's geoip and ipcidr decide whether a result is trustworthy. Here is the most common pattern in older configs:
# Original Clash syntax; mihomo still parses it, but it is not recommended going forward
dns:
enable: true
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
nameserver:
- 223.5.5.5
fallback:
- https://1.1.1.1/dns-query
fallback-filter:
geoip: true
ipcidr:
- 240.0.0.0/4
mihomo splits resolution into several paths: default-nameserver resolves only the domain names of the DNS servers themselves and must contain IPs; proxy-server-nameserver resolves proxy server addresses; direct-nameserver handles direct traffic; and nameserver-policy assigns upstreams by domain or rule set. The split solves one problem — which path resolves a proxy server's domain name directly determines whether the node can connect at all.
dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter-mode: blacklist
fake-ip-filter:
- "*.lan"
- "+.stun.*.*"
default-nameserver:
- 223.5.5.5
- 1.1.1.1
proxy-server-nameserver:
- https://223.5.5.5/dns-query
nameserver:
- https://1.1.1.1/dns-query
- quic://dns.adguard-dns.com:784
nameserver-policy:
"geosite:cn":
- 223.5.5.5
"rule-set:reject-list":
- rcode://refused
respect-rules: true
cache-algorithm: arc
Two behavior changes are worth noting separately: fake-ip-filter is blacklist-only in the original Clash, while mihomo adds fake-ip-filter-mode: whitelist, which flips it so fake IPs are assigned only to the domains on the list; and nameserver-policy keys accept geosite: and rule-set: prefixes, so you no longer maintain a long fallback-filter.domain list. Also, respect-rules: true sends DNS queries themselves through the rules, and proxy-server-nameserver must be configured first or resolution requests can loop back on themselves.
TUN implementation and the three network stacks
The original open-source Clash has no TUN at all; TUN existed only in the closed-source Premium core, with very few tunable options. mihomo treats TUN as a first-class part of the config, covering everything from device name and MTU to per-package allow lists on Android.
tun:
enable: true
stack: mixed
device: mihomo
mtu: 9000
auto-route: true
auto-detect-interface: true
strict-route: false
dns-hijack:
- any:53
udp-timeout: 300
endpoint-independent-nat: false
gso: true
gso-max-size: 65536
Trade-offs between the three stack values:
- system: packets are forwarded by the system network stack, giving the highest throughput, at the cost of depending on the system's routing and firewall state — loopback or leaks are more likely with multiple NICs and IPv6.
- gvisor: a pure userspace implementation with consistent behavior across platforms and no reliance on system forwarding; single-connection throughput is lower than system, which suits environments with complex routing tables or restricted permissions.
- mixed: TCP goes through the system stack and UDP through the gvisor stack. It is the default in most clients and a good intermediate step to test before switching from gvisor to system.
Platform requirements differ: Windows needs the wintun driver and system service permissions, and a failed virtual adapter shows configure tun interface in the log; macOS uses a utun device and asks for network permission the first time it is enabled; on Linux, auto-route writes to the routing table and auto-redirect uses nftables to redirect traffic into the TUN, so no hand-written iptables rules are needed; Android clients use include-package and exclude-package to control which apps go through the TUN.
Performance trade-offs and migration order
The cost of rule matching depends mainly on type and count. Domain rules use an index and cost very little per rule; DOMAIN-REGEX and PROCESS-NAME-REGEX are evaluated one by one, and placing them at the top of rules slows the first packet of every new connection. A few connection-side switches are worth enabling as needed:
- tcp-concurrent: true — handshakes concurrently with multiple resolved addresses for the same domain, shortening the wait for the first packet at the cost of more concurrent connections.
- unified-delay: true — latency tests all measure the full handshake time, so numbers from different protocols are comparable.
- sniffer section — recovers domain names from IP-only traffic so domain rules can match; override-destination rewrites the destination to the recovered domain, which some LAN setups need to turn off.
- find-process-mode: strict — process matching is queried once when a new connection is established, which uses less CPU than always.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: warning
ipv6: false
unified-delay: true
tcp-concurrent: true
find-process-mode: strict
external-controller: 127.0.0.1:9090
profile:
store-selected: true
store-fake-ip: true
sniffer:
enable: true
sniff:
HTTP:
ports: [80, 8080-8880]
override-destination: true
TLS:
ports: [443, 8443]
QUIC:
ports: [443]
Migrating from the original Clash to mihomo can follow a fixed six-step order, and each step can be verified on its own:
-
Back up the old config
Copy config.yaml and the ruleset directory as a whole, and note the old core's version string so you can compare when rolling back.
-
Validate the syntax first
Run mihomo -t -f config.yaml -d /etc/mihomo for a parse-only check; unsupported proxy type surfaces here, so you do not have to wait for the log after startup.
-
Handle the proxy section
Keep hysteria2, tuic, and vless nodes and verify each field name; ss nodes using 2022-blake3- ciphers require core version support, and older cores reject them outright.
-
Rewrite DNS
Add default-nameserver and proxy-server-nameserver, move the fallback list into nameserver-policy, and leave fake-ip-range at 198.18.0.1/16.
-
Enable TUN last
Confirm the driver and service are ready first, start with stack: mixed, use dns-hijack: any:53, and only consider switching to system once routing and resolution both check out.
-
Watch connections and rule hits
Turn on external-controller: 127.0.0.1:9090 and check the connection list and rule hits in the dashboard to confirm traffic is not all falling through to MATCH.
There are also cases where staying on the original Clash makes sense: if the config only uses ss, vmess, and trojan, all rules live in the rules section, and there is no need for TUN, then switching cores brings limited benefit and adds risk. The test is simple — if any of the four areas above uses syntax only mihomo supports, switch; if not, leave it alone.