Route developer services by destination, not by application name
For reliable GitHub, npm, and Docker Hub access, match the service’s domains before the catch-all rule, and send those matches to a proxy group that actually has a working node. A Clash rule does not recognize “GitHub Desktop,” an npm command, or a Docker process as a special application. It evaluates connection information—such as a domain name or destination IP—and then selects a policy. That makes domain rules a practical starting point for developer traffic, provided the client can see the domain and the application’s traffic enters the Clash core.
Keep the routing policy separate from the proxy itself. Your subscription may supply a group named Proxy, Auto, or something else; the examples below use My Subscription as a stand-in. Replace it with a group that exists in your active config. A rule can match correctly and still fail if its target group is missing, has no usable node, or is set to DIRECT.
- GitHub: account pages, the API, raw files, and release downloads can use different hostnames. Cover the domains you need rather than assuming one hostname represents every request.
- npm: registry requests usually go to
registry.npmjs.org, but project-specific registry settings and package download hosts can change the destination. - Docker Hub: login, manifest, and layer requests may use separate hosts. A successful browser sign-in does not prove that a Docker image pull follows the same route.
Use rule providers for maintained domain lists
Inline rules are convenient for a handful of stable domains. A rule-provider is more useful when a list has several entries, changes independently of the main config, or needs to be reviewed and updated on a schedule. In mihomo, a provider declares a source, a local cache path, a format, and a behavior. The behavior tells the core how to interpret the provider’s entries: domain for domain matching, ipcidr for IP ranges, or classical for a list of full Clash-style rules.
The following is a structural example, not a ready-to-import source: rules.example.net is a reserved example domain. Replace each URL with a trusted rule-list endpoint that returns a compatible YAML payload. Keep separate providers for services with different update needs so a change to a broad GitHub list does not unexpectedly alter Docker routing.
rule-providers:
dev-github:
type: http
behavior: domain
format: yaml
url: "https://rules.example.net/github-domains.yaml"
path: ./rules/dev-github.yaml
interval: 86400
dev-npm:
type: http
behavior: domain
format: yaml
url: "https://rules.example.net/npm-domains.yaml"
path: ./rules/dev-npm.yaml
interval: 86400
dev-docker:
type: http
behavior: domain
format: yaml
url: "https://rules.example.net/docker-domains.yaml"
path: ./rules/dev-docker.yaml
interval: 86400
For a provider with behavior: domain and format: yaml, the response should use a payload list. A minimal GitHub list could look like this:
payload:
- "+.github.com"
- "+.githubusercontent.com"
- "+.githubassets.com"
The +. form is a domain-suffix entry that covers the base domain and its subdomains. This example covers common GitHub web and content hosts, but no domain list is universal: validate the actual hostnames shown by the client’s connection log when a specific operation still fails. Avoid adding broad cloud-hosting domains just because one release download uses a hosted asset; those domains may serve many unrelated customers.
In the main rules list, reference a provider with RULE-SET. The provider name must match the key under rule-providers, and the policy name must match a proxy group in the active config.
rules:
- RULE-SET,dev-github,Developer Proxy
- RULE-SET,dev-npm,Developer Proxy
- RULE-SET,dev-docker,Developer Proxy
- MATCH,My Subscription
Providers are evaluated where they appear in the rules list. Put specific developer-service rules before broad rules such as a general private-network rule or GEOIP,CN,DIRECT when you intend the developer destinations to take priority. The final MATCH rule is a fallback; it should come after the rules whose behavior you want to preserve.
Build a small, inspectable ruleset for GitHub, npm, and Docker
You do not need a provider for every domain. For a short list of known endpoints, inline DOMAIN-SUFFIX rules are easier to inspect and reduce dependency on a remote rule-list service. The example below shows common destinations; treat it as a starting point, not a guarantee that every GitHub, npm, or Docker request uses only these hosts.
proxy-groups:
- name: Developer Proxy
type: select
proxies:
- My Subscription
- DIRECT
rules:
# GitHub web, API, raw content, and release assets
- DOMAIN-SUFFIX,github.com,Developer Proxy
- DOMAIN-SUFFIX,githubusercontent.com,Developer Proxy
- DOMAIN-SUFFIX,githubassets.com,Developer Proxy
# npm registry
- DOMAIN,registry.npmjs.org,Developer Proxy
- DOMAIN-SUFFIX,npmjs.org,Developer Proxy
# Docker Hub authentication, registry, and common layer delivery
- DOMAIN-SUFFIX,docker.io,Developer Proxy
- DOMAIN,production.cloudflare.docker.com,Developer Proxy
# Preserve the rest of the profile's routing policy
- MATCH,My Subscription
DOMAIN matches one exact hostname; DOMAIN-SUFFIX matches a domain and its subdomains. In this example, the docker.io suffix covers hosts such as registry-1.docker.io and auth.docker.io. The separately listed Cloudflare hostname is deliberately exact: routing every domain under cloudflare.com through a developer group would affect a large amount of unrelated traffic.
The group gives you a choice between a subscription group and a direct connection. If this config should always proxy developer services, remove DIRECT from the group. If you keep it, remember that a manual selection of DIRECT overrides the expectation that these rules always use a proxy. A select group does not choose the fastest node automatically; choose an existing automatic or load-balancing group instead if that is the intended behavior and your core supports it.
- Keep group names exact: YAML references are not comments or labels. A typo in
My Subscriptioncan stop the config from loading. - Do not discard the subscription’s rules: Add developer-specific rules to the intended override or merge layer, and retain the rest of the profile’s routing policy.
- Check your core:
rule-providers, provider formats, and other newer fields depend on the actual Clash-family core and version used by the client.
Apply the changes and verify each service step by step
Change one layer at a time. If you edit a large subscription config, save a copy first so you can return to the last working version. A clear test sequence separates YAML errors, provider download failures, routing mistakes, and application-specific proxy behavior.
- Confirm the active core and config. In the client’s About, Core, or profile screen, note the core name and version, the active profile, and its last successful reload. Do not assume that the client’s app version identifies the core version.
- Add the group before referencing it. Verify that
Developer Proxyexists and that the referenced subscription group exists. If you use providers, add their definitions and check that their URLs return YAML in the format declared byformat. - Validate and reload. Use the client’s config test or reload control. Resolve parser errors before testing an application. Common causes include inconsistent indentation, a missing colon, an incorrect provider key, and an unsupported field in an older core.
- Test a single destination. Open a GitHub page or request the npm registry, then inspect the client’s Connections or Logs view. Check the displayed host, matched rule, selected policy group, and final outbound node. A successful connection to the website alone does not identify which rule handled it.
- Test Docker separately. Run a small image pull and inspect the log while it is in progress. Docker CLI commands can delegate pulls to the Docker daemon or Docker Desktop, so verify that the daemon’s traffic enters the proxy core. If it does not, configure the supported proxy setting in Docker Desktop or the daemon rather than repeatedly changing domain rules.
- Record the result and keep a rollback. Save the working config revision and note which hostname and rule handled each test. This gives you a baseline for later changes to subscriptions, clients, or rule lists.
For quick checks, curl -I https://github.com and npm ping can confirm that a particular command-line request gets a response, but neither proves that every host used by a release download or package install is reachable. For Docker, a successful login and a successful image pull are separate tests: image layers may be retrieved from different endpoints than the authentication request.
Troubleshoot mismatches and keep the rules maintainable
When a service fails, first identify the host that failed rather than adding more guessed domains. A browser’s address bar may show github.com, while the connection log reveals a raw-content, API, or asset host. During a Docker pull, authentication, registry metadata, and layer downloads may each create separate connections. Add a hostname only after the log or the application’s diagnostic output shows that it is relevant.
- The config fails to load: Check indentation and commas in inline rules, then verify that every
RULE-SETname has a matching provider definition. Confirm that the core supports the chosen providertype,behavior, andformat. - The provider does not update: Check whether its URL is reachable from the current network, whether it returns YAML rather than an HTML error page, and whether the declared format matches the payload. A cached file may let an earlier config continue to work while a refresh fails.
- The connection uses the wrong policy: Check rule order, exact domain spelling, and whether an earlier rule already matched. Then confirm the policy group’s current selection. The first matching rule determines routing; a later developer rule cannot override an earlier match.
- The log shows an IP address instead of a hostname: Domain matching may be unavailable for that connection, for example because of how the client handles DNS or because the application connects directly to an IP. Review DNS and sniffing behavior for your core, and use an IP rule only when you can maintain the range safely.
- The rules match but the service still times out: Check the selected node, outbound connectivity, DNS resolution, and whether the service requires UDP or another protocol. A correct rule cannot repair a broken proxy node or a server-side access restriction.
- Only one development tool fails: Inspect that tool’s own proxy configuration. npm can use a custom registry or environment-based proxy settings; Docker Desktop and its daemon can have separate proxy configuration. These settings can alter or bypass the path you expected Clash to handle.
Keep rule lists narrow and reviewable. A dedicated GitHub, npm, or Docker provider makes it easier to see why a hostname was added, and a private version-controlled config lets you compare changes and roll back a bad update. Do not commit subscription URLs, passwords, or tokens. If a remote provider is critical to your setup, monitor its response and keep a known-good local copy or a simple inline fallback strategy appropriate to your client.
Finally, retest after updating the client or core. Config compatibility, DNS behavior, TUN integration, and provider handling can change independently. Record the core version alongside any report of a rule problem; “the Clash config stopped working” is not enough information to distinguish a parser change from a network, DNS, or application-proxy issue.