Separate the client, core, and configuration format
mihomo is a proxy core, formerly known as Clash Meta. It parses configurations, establishes outbound connections, matches rules, and handles DNS. The client provides features such as configuration import, system proxy controls, TUN mode, proxy group selection, and logs. A client with “Clash” in its name does not necessarily run the original Clash core or support every mihomo field.
When comparing it with “original Clash,” distinguish the open-source core from the historical Clash Premium. They differed in some features, particularly TUN and rule providers. So it’s inaccurate to describe every extension as “first supported by mihomo,” or to judge core capabilities solely by the interface of an older client.
- Client version: Determines the interface, permission controls, and how configuration overrides work.
- Core version: Determines which protocols and fields are supported, and which networking features are available.
- Subscription format: Determines whether the import contains only proxy nodes or a full configuration with DNS, rules, and proxy groups.
Protocol support: parsing a node doesn’t guarantee it will connect
Compared with the original open-source Clash, mihomo supports more outbound protocols and transport options. Common reasons to migrate include VLESS, REALITY, Hysteria2, and TUIC. The available fields still depend on your installed core version. A subscription converter set to an older Clash output format may also strip these nodes or parameters before they reach the client.
| Capability | mihomo configuration details | Common migration issues |
|---|---|---|
| VLESS | type: vless, UUID, transport, and TLS settings | The subscription converter filters out the node type or omits transport parameters |
| REALITY | TLS、servername、reality-opts | The public key or short ID doesn’t match the server configuration |
| Hysteria2 | type: hysteria2, authentication, TLS, and UDP reachability | UDP is blocked by the network, or fields for the original Hysteria protocol are used by mistake |
| TUIC | type: tuic, authentication, and server protocol version | The client and server have incompatible version requirements |
REALITY is not a standalone node type
Don’t use type: reality as a substitute for VLESS. A common setup enables TLS on a VLESS node and configures reality-opts. If the server uses Vision, check the corresponding flow setting too. Seeing a node in the proxy list only means the configuration was read; it doesn’t guarantee authentication or the handshake will succeed.
Test one node before migrating the whole group
- Make sure the subscription output supports mihomo or Clash Meta, not just older Clash versions.
- Check that the node type, server port, authentication fields, and TLS settings are complete.
- Test a known-good node against the destination site, then check the logs for handshake or authentication errors.
- For Hysteria2 or TUIC, check UDP connectivity separately. A successful TCP port test does not verify either protocol.
Updating the core won’t automatically convert old nodes to a new protocol or change the server configuration. Whether a new protocol is a good fit depends on server support and your network—not on how recently the protocol was added.
rule-providers: separate rule sources from rule execution
rule-providers manages standalone rule sets, which are then referenced in the rules list using RULE-SET. Historical Clash Premium already supported rule providers, and mihomo has expanded the supported formats and related features. That means migration steps differ depending on whether you’re coming from open-source Clash or Premium.
This example uses a local rule set to rule out remote download failures. Both files are examples. Merge the main configuration snippet into your existing setup; replacing it outright would discard your current nodes, proxy groups, and DNS settings.
Main configuration snippet
rule-providers:
local-direct:
type: file
behavior: classical
format: yaml
path: ./rules/local-direct.yaml
rules:
- RULE-SET,local-direct,DIRECT
- MATCH,DIRECT
Rule set file: rules/local-direct.yaml
payload:
- DOMAIN-SUFFIX,example.com
- IP-CIDR,192.168.0.0/16,no-resolve
This example only demonstrates referencing a rule set; it sends all traffic direct and is not a ready-to-use proxy routing setup. path is resolved relative to the core’s working directory. GUI clients may use their own configuration directory, so check the active configuration or logs to find the actual path.
behavior: domainis for domain rule sets; the file should not contain fullDOMAIN-SUFFIX,...rule lines.behavior: ipcidris for IP CIDR rule sets. Don’t mix in domain rules.behavior: classicalsupports traditional rule entries like those in the example, subject to the rule types supported by the core.formatmust match the actual file format. mihomo’smrsformat is for domain and IP CIDR rule sets; renaming an arbitrary YAML file to use the MRS extension won’t convert it.
Rules are still evaluated in order. If MATCH comes before RULE-SET, later rule sets never get a chance to match. For remote providers, check separately that the download succeeds, the file parses, and requests match the rules—these are three different things.
DNS changes: distinguish destination domains from proxy server domains
mihomo offers more control over DNS resolution paths, including nameserver-policy, proxy-server-nameserver, and options that make DNS connections follow routing rules. The common migration pitfall isn’t a missing address; it’s a circular dependency where a DNS query needs to use a proxy whose server hostname hasn’t been resolved yet.
Start with a minimal, observable configuration
This snippet shows how fake-IP handling and proxy server hostname resolution serve different purposes. 127.0.0.1:1053 is a local DNS listening address, not a system proxy port. Setting a listener alone won’t make every app use it. Verify that the public resolvers in the example work on your network.
dns:
enable: true
listen: 127.0.0.1:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- "*.lan"
- "localhost"
nameserver:
- 223.5.5.5
proxy-server-nameserver:
- 223.5.5.5
nameserver handles regular domain lookups; proxy-server-nameserver is specifically for resolving proxy server hostnames. Both point to the same resolver here to illustrate their roles, not as a recommendation for every setup. With encrypted DNS, also check bootstrap resolution for the resolver’s own hostname and confirm it’s reachable.
fake-ip-filter is not a direct-connection rule
In fake-IP mode, the core returns synthetic addresses to apps and maintains the mapping between those addresses and domain names. Adding a domain to fake-ip-filter tells the core not to assign it a fake IP; it does not guarantee the connection will go direct. The final outbound route is determined by rules, proxy groups, and the selected mode.
When migrating, avoid changing upstream DNS, enabling TUN and sniffing, and rewriting all your rules at the same time. First confirm DNS returns the expected response, then check that connections match the right rules. If you need respect-rules, configure proxy server hostname resolution first and check the documentation for your core version, so DNS requests don’t depend on a proxy that isn’t up yet.
Sniffing: identify domain names, not decrypt HTTPS
When traffic reaches the core with only a destination IP, domain-based rules may not have enough information to match. mihomo’s sniffer can try to identify the domain using details such as the HTTP Host header or the server name in a TLS handshake. It doesn’t need to decrypt HTTPS payloads, and it can’t guarantee that every encrypted connection will reveal a domain name.
sniffer:
enable: true
sniff:
HTTP:
ports: [80, 8080]
TLS:
ports: [443]
This example attempts HTTP and TLS sniffing only on specified ports. It doesn’t enable extra behavior such as destination address overriding. Adjust the ports if your services use different ones. Don’t carry historical sniffing fields from an old configuration straight into the new structure. If the field hierarchy doesn’t match, use the current core’s configuration validation results as your guide.
- Useful when: TUN captures an IP connection that you expect to route by domain.
- Doesn’t replace: DNS configuration, proxy group definitions, route exclusions, or an app’s own proxy settings.
- Limitations to keep in mind: ECH and other mechanisms can hide the server name, and some traffic has no identifiable domain.
If enabling sniffing changes how a particular site behaves, first narrow sniffing to that domain or temporarily turn it off, then compare the connection logs. Don’t broaden the sniffed port range to mask DNS or routing issues.
TUN and system proxy: updating the core doesn’t change how traffic enters
The system proxy only works for apps that honor the operating system’s proxy settings. TUN mode uses a virtual network interface to capture IP traffic, and requires the client to handle permissions, routes, and DNS correctly. mihomo supports TUN, but setup varies by platform, client, and version. Pasting in a tun: configuration alone doesn’t mean all traffic is being captured.
For example, if the local mixed port is set to 7890, apps that honor the system proxy can send traffic to the core through that port. The DNS listener on 1053 is a separate entry point. They serve different purposes; don’t set the system HTTP proxy address to the DNS listening port.
For an initial migration, turn off TUN and use the system proxy to test your subscription and nodes, then enable TUN separately. If you run into issues with LAN devices, printers, or another VPN, check for overlapping routes, DNS capture, and exclusions before suspecting the node protocol.
Migrate an old configuration: check syntax, resources, and connectivity
Layer 1: inspect the final runtime configuration
Back up your subscription file, custom overrides, and proxy group selections first. A client may merge multiple sources before starting the core, so the file open in your editor may not be the configuration actually in use. Check the runtime configuration exported by the client and confirm that dns, rules, and proxy-groups haven’t been overridden with different content.
mihomo -v
mihomo -t -f ./config.yaml
-t checks configuration syntax. Passing the check means the core accepts the configuration; it doesn’t mean remote rules will download or nodes will connect. If the client manages the core, use its built-in validation option when possible to avoid running a different binary version.
Layer 2: verify external resources and name references
- Check that the nodes or providers referenced by proxy groups exist and that their names match exactly.
- Check that the
RULE-SETname matches a key underrule-providers. - Check rule file paths, remote resource requests, and parse errors to distinguish network failures from format problems.
- Make sure custom configuration still applies after subscription updates. Don’t edit a cached file that will be overwritten on the next update.
Layer 3: verify the result with a fixed request
If the mixed port is set to 7890 and the core is running, use the command below to send a request explicitly through the HTTP proxy. This avoids ambiguity about whether the test tool honors the system proxy.
curl --proxy http://127.0.0.1:7890 --connect-timeout 10 --max-time 20 -I https://example.com/
The 10-second and 20-second limits apply to connection setup and the overall request, respectively; they aren’t speed ratings. After the test, check the destination domain, matched rule, and actual outbound in the connections view. For errors, check the logs. If the handshake fails, check node parameters; if the wrong rule matches, check rule order and domain information; if there’s no connection entry, check the traffic entry point first.
A migration is complete when configuration validation passes, required resources load, app traffic reaches the core, rules match as expected, and nodes connect. Checking these five items one at a time makes issues easier to pinpoint than enabling every new feature at once—and keeps the old configuration available as a fallback.