Core Overview: Configuration Boundaries Between Clash Meta and mihomo

mihomo is a proxy core descended from Clash Meta. The client manages configuration, system proxy settings, and the user interface; the core handles incoming connections, evaluates rules, and establishes proxy connections. A client with “Clash” in its name does not necessarily use mihomo. Likewise, the ability to import YAML does not guarantee support for every protocol or extension field in that YAML.

“New features” here refers to capabilities added beyond the early Clash open-source core, not features introduced by a particular release on a specific date. Rule providers are also implemented in forks such as Clash Premium, so rule-providers should not be treated as a mihomo-only field. When migrating, check the core actually in use rather than judging by the client’s appearance.

CapabilityMain configuration optionRequirements
External rule setsrule-providers、RULE-SETThe rule format must match the behavior, and the resource must be accessible
Domain sniffingsnifferThe connection must reach the core and contain identifiable domain information
VLESS outboundtype: vlessThe server protocol, transport, and TLS settings must match
Hysteria2 outboundtype: hysteria2The server must support Hysteria2, and the network must allow the required UDP traffic

First, identify the core that is running

For a standalone deployment, run the following commands. In a graphical client, check the core information page and startup logs first. A program with the same name in your system terminal may not be the binary the client actually runs.

mihomo -v
mihomo -t -f config.yaml

Use -v to check the core version and -t to test a specified configuration. A configuration test can catch many invalid fields and references, but it cannot confirm that a remote node is reachable or replace actual DNS, TCP, and UDP tests. If a field is unrecognized, save the full version output and compare it with the configuration documentation for that release.

rule-providers: Separate rule content from routing policy

Writing a long list of domains directly into rules makes maintenance harder. Rule providers move the set of match criteria into a separate resource, leaving the main configuration to define the set name, update method, and resulting action. A rule set is not a proxy subscription: the former supplies match criteria, while the latter supplies proxy servers. They are not interchangeable.

HTTP rule sets and RULE-SET references

This example uses a public domain rule set to show how the fields fit together. The resource is maintained by its respective project; review its contents and intended use before deploying it. The example sends matches to REJECT and direct-connects everything else. It demonstrates rule set loading, not a complete routing setup for mainland China and elsewhere.

mode: rule
log-level: info

rule-providers:
  reject-domains:
    type: http
    behavior: domain
    format: yaml
    url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/reject.txt
    path: ./ruleset/reject-domains.yaml
    interval: 86400

rules:
  - RULE-SET,reject-domains,REJECT
  - MATCH,DIRECT
  • type: http: Fetches the resource over HTTP or HTTPS. The resource must be reachable the first time it is used.
  • behavior: domain: Treats the contents as a domain set, not as complete rule statements.
  • format: yaml: Specifies the resource format. A URL ending in .txt does not necessarily point to a plain-text file.
  • path: The local cache location. Make sure the core's working directory is writable, and give each rule set its own file.
  • interval: 86400: Sets the update interval to 86,400 seconds (24 hours), not milliseconds.

The behavior must match the file contents

Use domain for a set of domains, ipcidr for a set of IP ranges, and classical for rule statements such as DOMAIN-SUFFIX and IP-CIDR. You cannot use the same file for all three by simply changing the behavior name. The following shows the structure of a local classical YAML file and how it differs from a plain domain list:

payload:
  - DOMAIN-SUFFIX,example.com
  - IP-CIDR,192.168.50.0/24,no-resolve

Here, example.com is a reserved example domain, and 192.168.50.0/24 is an example private subnet. This file lists match criteria only; whether traffic goes direct, through a proxy, or is rejected is determined by RULE-SET,collection-name,policy-name in the main configuration. For different routes, split the criteria into separate sets instead of appending policy groups to individual set entries.

Domain sniffing: Derive domain clues from connection traffic

When a connection reaches the core by destination IP, domain-based rules may not have enough information to match. Sniffing attempts to identify the destination domain from the HTTP Host header, the SNI visible in a TLS ClientHello, or information in supported QUIC handshakes. It does not decrypt HTTPS, reveal encrypted webpage content, or guarantee that every connection's domain can be identified.

HTTP, TLS, and QUIC configuration example

sniffer:
  enable: true
  force-dns-mapping: true
  parse-pure-ip: true
  override-destination: false
  sniff:
    HTTP:
      ports: [80, 8080]
    TLS:
      ports: [443, 8443]
    QUIC:
      ports: [443]

ports specifies the destination port range on which to try identifying the corresponding protocols. Listing only 443 will not cover every TLS service running on other ports. parse-pure-ip allows sniffing attempts for destinations specified by IP alone, while force-dns-mapping continues sniffing in relevant DNS-mapping scenarios. Neither option guarantees that a domain can be extracted.

The example sets override-destination to false so the sniffed domain does not directly replace the original destination, making it easier to observe changes in rule matching. Setting it to true changes how the connection destination is handled and should be tested separately. The identified domain may affect rule selection, but rules are still subject to the current mode and order. In global mode, this does not provide full domain-based routing.

How sniffing relates to Fake-IP and TUN

  • Fake-IP: The core maintains a DNS mapping between domains and fake addresses. When an application's DNS requests already pass through the core, the domain is usually available, making sniffing a supplementary mechanism.
  • TUN: Routes network traffic through a virtual network interface. It expands the range of traffic captured but does not automatically attach a domain to every connection.
  • Sniffing: Processes connections that have already reached the core. It does not capture traffic that bypasses the system proxy and does not pass through TUN.
  • Identification limits: Encryption mechanisms such as ECH, nonstandard handshakes, missing SNI, and unsupported protocols can all prevent the expected domain from being identified.

When troubleshooting, first confirm that the connection appears in the client's connection list, then check its destination IP, identified domain, matched rule, and final policy. Adjusting sniffer only helps after the connection reaches the core. If an app's traffic is not being captured, check the system proxy or TUN routing first rather than continually expanding the sniffing port range.

VLESS: Check the protocol, transport, and TLS settings separately

VLESS is a proxy protocol, WebSocket is an optional transport, and TLS is the connection security layer. They are not controlled by the same setting. If the server uses VLESS over WebSocket over TLS, the client must match its UUID, port, WebSocket path, Host, and TLS server name.

The example below shows the field structure using a reserved example domain and a demo UUID; it does not connect to a working service. For a real deployment, use the complete parameters provided by the server. Do not guess the path or disable certificate verification to work around connection errors.

proxies:
  - name: VLESS-WS-TLS
    type: vless
    server: vless.example.com
    port: 443
    uuid: 550e8400-e29b-41d4-a716-446655440000
    network: ws
    tls: true
    servername: vless.example.com
    udp: true
    ws-opts:
      path: /proxy
      headers:
        Host: vless.example.com

Common parameter mismatches

  1. Transport mismatch: If the server uses TCP, changing the client to network: ws will not fix the problem; changing the port will not help either.
  2. Confusing domain fields: server is the connection address, servername is used for TLS, and the WebSocket Host is used in the HTTP request. Their values may differ depending on the deployment, but they are not interchangeable.
  3. Path mismatch: A reverse proxy may route /proxy and other paths to different services. Use the path and capitalization required by the server.
  4. Fields from other setups: REALITY, public keys, fingerprints, and flow each have specific use cases. Do not add them to a standard WebSocket-over-TLS example without a reason.

After adding a proxy entry, add its name to an existing policy group and reference that group in your rules. Listing a proxy under proxies alone does not mean requests will use it. If the client periodically replaces subscription configurations, use its supported override or merge feature to preserve custom entries and prevent them from disappearing on the next update.

Hysteria2: Check the UDP path before tuning transport settings

Hysteria2 is based on QUIC and requires UDP. Its configuration syntax differs from that of the original Hysteria; if a node is labeled Hysteria2, use type: hysteria2. Being able to connect to the server's TCP port 443 does not prove that its UDP service on the same port is reachable.

proxies:
  - name: HY2-TLS
    type: hysteria2
    server: hy2.example.com
    port: 443
    password: demo-password-for-documentation
    sni: hy2.example.com
    skip-cert-verify: false

This example also uses a reserved example domain and demo credentials. password must match the server's authentication settings, and sni must match the actual certificate and server deployment. If the server uses a specific obfuscation method, configure the corresponding fields too; do not assume they can be omitted just because the protocol name matches.

Connection timeout troubleshooting order

  • Confirm that the server address resolves to the expected IP, and check that both IPv4 and IPv6 paths work if applicable.
  • Confirm that the server listens on UDP 443 and that the host firewall, cloud security group, and intermediate gateways allow the traffic.
  • Check the credentials, SNI, certificate validity period, and device clock.
  • Test on another network to check whether UDP is restricted on the current network.
  • Once the basic connection works, adjust bandwidth or obfuscation settings as required by the server, changing only one category at a time.

Hysteria2 performance depends on packet loss, congestion, ISP policies, and server resources. The protocol name alone does not guarantee a particular throughput. For a meaningful comparison, use the same device, network, and destination, and record connection success and transfer performance separately. Do not generalize from a single speed test to every environment.

Configuration checks: Validate parsing, resources, connections, and rules in order

Rule sets, sniffing, and new protocols can be combined, but it is best not to enable everything at once when migrating. Back up the original configuration, keep one verified working route, and make changes one at a time. The steps below help distinguish configuration loading failures from node connection failures.

  1. Parsing: Run mihomo -t -f config.yaml and fix indentation, duplicate top-level keys, unknown proxy types, and references to nonexistent policy groups.
  2. Resources: Start the core and check whether rule sets download successfully. Troubleshoot HTTP errors, write permissions, and content format mismatches separately.
  3. Connections: Select a single proxy node and test the handshake first. For VLESS, focus on transport and TLS; for Hysteria2, check UDP and authentication.
  4. Rules: Switch back to rule mode and check whether the target connection matches the expected RULE-SET and whether its final route matches the policy group's current selection.

Verify the local proxy with an explicit port

If the test configuration sets mixed-port: 7890, use the request below to verify the local HTTP proxy. mixed-port accepts both HTTP and SOCKS proxy connections. This is not the node's server port or the external controller port.

curl -I --proxy http://127.0.0.1:7890 https://example.com

This command only verifies how this specific request was handled. Check the connection log too to confirm whether it used a proxy node or DIRECT; receiving a webpage response alone does not prove that routing is configured correctly. You can temporarily set log-level to debug during testing, then restore it to info. Before sharing logs, remove subscription URLs, credentials, and personal browsing records.

Keep top-level YAML keys unique when merging

The code blocks in this article show separate feature snippets; do not concatenate them at the end of a file without organizing them. If proxies already exists, add the new node to the existing list. If rules already exists, insert rules in priority order. Defining the same top-level key more than once may cause an error or cause one definition to overwrite another. Do not rely on either behavior to merge configurations.

A migration is complete when the core version is clear, the configuration test passes, rule resources are available, the target protocol completes its handshake, and connection logs show the expected rule and route. If you need system-wide traffic capture, enable TUN separately, then retest LAN access, DNS, and app connections. For basic setup, see this site's configuration guide; for terminology, see the quick glossary.