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.

Reverse migration has pitfalls too Moving mihomo node configs back to the original core leaves client-fingerprint, reality-opts, and packet-encoding as unknown fields. Tolerance varies by version: some exit with a parse error, others ignore them and connect with defaults — the nodes show up but every latency test times out, which is easy to misdiagnose as a network problem.

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:

  1. 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.
  2. 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.
  3. 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.

The easiest item to miss when migrating fallback and fallback-filter are still parsed by mihomo and will not stop it from starting, so many people update the proxy section and TUN but forget proxy-server-nameserver. The typical symptom: the core starts fine, the log shows no errors, yet every node times out, because proxy server domains are resolved through a path that does not work.

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.

How resolution behaves in TUN mode Once TUN is on, dns-hijack hands queries sent to port 53 to the core's DNS, so which path resolution takes is decided by the dns section, not by system DNS settings. During debugging, setting dns-hijack to any:53 avoids gaps caused by hijacking IPv4 only.

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.