Subscriptions & Config 12-minute read

Clash Subscription Formats: Convert Clash YAML, Base64 Node Links & sing-box Config

Compare Clash YAML, Base64 node links, and sing-box JSON. Learn what clients can import and how to convert subscriptions locally without losing fields or exposing credentials.

Identify the content before choosing an import method

A subscription URL is an address for retrieving content, not a format name. The same URL may return different content depending on request parameters or the client’s User-Agent. A file ending in .yaml is not necessarily a Clash config. If an import fails, inspect the actual response locally before repeatedly changing the file extension.

Keep three layers distinct: Base64 is an encoding, URIs such as ss:// describe individual nodes, and Clash YAML and sing-box JSON are configuration formats for their respective cores. Decoding a list of nodes does not produce a complete config with routing rules, DNS, and listener ports.

Content typeHow to identify it locallyWhat it usually contains
Complete Clash YAML configCommon top-level keys include proxies, proxy-groups, and rulesProxies, proxy groups, and rules; may also include DNS and TUN settings
Clash proxy-provider YAMLUsually centered on a top-level proxies: listA set of nodes referenced by proxy-providers
Node URI listMultiple lines of ss://, vmess://, trojan://, and similar URIsNode connection parameters, usually without a complete routing policy
Base64-encoded subscriptionLooks like a continuous string of letters, numbers, and encoding symbols; decode it to identify the contentsOften a list of node URIs; verify by inspecting the decoded output
sing-box JSONCommon top-level keys include inbounds, outbounds, and routeInbound and outbound connections, routing, and optional DNS settings

Clash YAML: Full Configs and Proxy Providers

Valid YAML does not necessarily mean a runnable proxy config

YAML represents data; mihomo also validates field types, node parameters, and references. Use spaces for indentation, never tabs. Enter ports as numbers where required. Quote names, passwords, or URLs containing special characters to prevent # from being treated as a comment or a colon from changing the structure.

This minimal direct-connection example illustrates the structure; it contains no proxy nodes. It listens on 127.0.0.1:7890, uses rule mode, and routes traffic to a proxy group named “Egress”. The group contains only DIRECT, so traffic still goes directly out. The example does not enable the client’s system proxy or TUN automatically.

mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info

proxies: []

proxy-groups:
  - name: Egress
    type: select
    proxies:
      - DIRECT

rules:
  - MATCH,Egress

Provider files must be referenced by a main config

Proxy-provider files usually contain only a proxies collection. A main config defines the source, update interval, and health checks under proxy-providers, then references the provider name with a group’s use field. Importing a provider file as a complete config may show the nodes but leave out the expected proxy groups.

  • Check references: Every policy name in a rule must exist, and every node or provider name in a proxy group must match.
  • Check for duplicate names: Duplicate node names make selection and references ambiguous. If you rename nodes during conversion, update every related reference.
  • Check core compatibility: A Clash-compatible config is not guaranteed to work with every older Clash core. Some protocols and extended fields require mihomo; support depends on the core used by the client.

Base64: Decoding Is Not Protocol Conversion

Base64 does not hide subscription credentials or fill in missing fields. A common subscription format joins multiple node URIs with newlines and encodes the entire text; some URIs may themselves contain Base64 segments. After decoding the outer layer, identify the protocol on each line. Don’t keep decoding everything until it “looks like text.”

Inspect the outer content locally with Python 3

Save the retrieved encoded content as subscription.txt, save the script below as decode_subscription.py, then run python3 decode_subscription.py. The script supports common standard and URL-safe alphabets and restores missing trailing padding. It only decodes; it does not connect to nodes, convert configs, or print node details to the terminal.

from pathlib import Path
import base64
import binascii

source = Path("subscription.txt").read_text(encoding="utf-8-sig")
compact = "".join(source.split())

if not compact:
    raise SystemExit("Input is empty")

compact += "=" * (-len(compact) % 4)

try:
    raw = base64.b64decode(compact, altchars=b"-_", validate=True)
    text = raw.decode("utf-8")
except (ValueError, binascii.Error, UnicodeDecodeError):
    raise SystemExit("Could not decode as UTF-8 Base64 text; check the original response first")

with Path("decoded.txt").open("x", encoding="utf-8") as output:
    output.write(text)

print("Wrote decoded.txt; inspect the content type locally")

Exclusive file creation prevents overwriting an existing decoded.txt. If the file already exists, make sure you no longer need it before proceeding. The decoded file still contains sensitive information; don’t upload it to a public code repository or share it in a group chat.

URIs still need to be parsed according to their protocol

  • ss:// has several representations. User information may be Base64-encoded and may include plugin parameters, so don’t parse it by splitting at the first colon or @.
  • A common vmess:// format contains encoded JSON, but its fields still need validation. It is not a generic node string that can be parsed arbitrarily.
  • Links such as trojan:// and vless:// often carry TLS, transport, and path details in query parameters. URL percent-decoding must also respect field boundaries.
  • The display name in a URI fragment is usually not a connection parameter. Renaming a node can make it easier to identify, but it won’t fix an incorrect server address or authentication details.

Conversion Limits Between sing-box JSON and Clash YAML

Both formats can describe proxy connections, but their config models differ. Clash typically organizes outbound traffic around nodes, proxy groups, and ordered rules; sing-box uses tagged tag outbounds referenced by routing rules. Renaming proxies to outbounds does not create a valid sing-box config.

This sing-box example shows a direct-connection setup: HTTP and SOCKS share 127.0.0.1:7890, and traffic is routed to direct-out. It is a standalone example for comparing fields; it cannot use the same listening address and port as the mihomo example above at the same time.

{
  "inbounds": [
    {
      "type": "mixed",
      "tag": "mixed-in",
      "listen": "127.0.0.1",
      "listen_port": 7890
    }
  ],
  "outbounds": [
    {
      "type": "direct",
      "tag": "direct-out"
    }
  ],
  "route": {
    "final": "direct-out"
  }
}
Conversion areaWhat needs to be handled
Node connectionsMap the protocol, address, port, authentication, TLS, and transport parameters individually
Proxy selectionClash proxy groups and sing-box selector outbounds differ in structure and available options
Routing rulesRule syntax, actions, and rule-set formats differ; rules cannot be copied as-is
DNSRebuild server definitions, DNS routing, Fake-IP, and bootstrap resolution separately
Traffic captureSystem proxy settings are managed by the client or OS; TUN also involves permissions, routing, and platform-specific behavior

Record versions alongside the config

sing-box DNS and routing fields have changed across versions. For example, sing-box 1.12.0 introduced a new DNS server config format. A config that imports into one client may not work with a newer core. Before converting, record the source client core version, target core version, and converter version. Generate the config against the documentation for the target version, not just the version shown in the app.

Client format support: distinguish the importer from the core

“Client supports a format” may mean it has a built-in import or conversion feature—not that its underlying core reads that format directly. Clients powered by mihomo typically use Clash YAML. Some clients can turn node links into a config, but support for a full Base64 subscription and preservation of rules and DNS depend on the specific import method. The sing-box core primarily reads its own JSON config; graphical clients may implement separate subscription management features.

  1. Check what the import option does: Distinguish between “import config from URL,” “add a single node,” “import a proxy provider,” and “import a local file.” A single-node import usually won’t create a complete set of routing rules.
  2. Review the import preview: Check the node count, protocol types, and proxy group names. If the source has 12 nodes but the import shows only 9, investigate the 3 missing nodes before enabling it.
  3. Check the active config: The subscription cache, user overrides, and client-generated config may be three separate files. Verify separately whether changes made in the UI will remain after the next subscription update.

If you need a different client, browse the download center by operating system. For importing a subscription and routing traffic for the first time, see the subscription import guide. Don’t assume a client supports every Clash extension based on its name alone.

Convert and validate locally: narrow down issues in five steps

1. Save the source file and target version

Keep an untouched copy of the source file and save the converted result separately. Record when it was retrieved, its content type, and the target core version. If the subscription URL includes an access token, record only what it’s for—not the full URL—in shared documentation.

2. Convert nodes first, then migrate policies

First verify the server, port, password or UUID, encryption method, and transport parameters. Then handle groups and rules. If the target core cannot represent a protocol, record the failure instead of silently replacing it with another protocol. If you only have a node list, design DNS and routing separately; a converter cannot restore rules that were never included.

3. Validate syntax with the target core

These commands assume the core executable is available in the current terminal path. mihomo’s -t checks the config; sing-box’s check validates it. On Windows, use the corresponding .exe filename. A successful check means the config is valid at the syntax level; it does not confirm server reachability or successful authentication.

mihomo -t -f converted.yaml
sing-box check -c converted.json

4. Test nodes through an explicit proxy first

To reduce interference from system settings, test through a confirmed local HTTP proxy port before enabling TUN. The example requires port 7890 to be present in the config, with the node under test selected in the active policy. The test site is the request destination, not the proxy server.

curl --proxy http://127.0.0.1:7890 --connect-timeout 10 --max-time 20 -I https://example.com/

A successful command only confirms that this request reached its destination through the local proxy. Check the connection panel or logs to verify the actual outbound route: if it hit DIRECT, that does not prove the proxy node is working. If the request fails, check in order for a port that isn’t listening, DNS failure, TLS handshake failure, or remote authentication rejection.

5. Test system proxy and TUN afterward

Once the explicit proxy test works, enable the system proxy and test in a browser. Only test TUN if you need to capture traffic from apps that ignore system proxy settings. Issues with TUN permissions, routing, or DNS capture are not subscription encoding problems. Change one layer at a time to tell whether an issue comes from node conversion or traffic capture.

Protect subscription credentials and converted files

Giving a subscription URL to an online converter usually lets that service access the original subscription and its node credentials. Opening a conversion page in a browser does not prove the conversion happens locally. For offline processing, use a local tool confirmed not to rely on a remote service, and check whether additional resources such as rule templates or remote providers still require an internet connection.

  • Restrict local interfaces: If a conversion service is for local use only, have it listen on 127.0.0.1. Don’t expose it on 0.0.0.0 or a public port.
  • Limit file access: On Linux or macOS, use chmod 600 subscription.txt decoded.txt converted.yaml to restrict access to existing files. On Windows, set file security permissions.
  • Redact before troubleshooting: When sharing an error, keep the protocol, field names, and error location, but remove subscription tokens, passwords, UUIDs, and any server details that should remain private.
  • Replace exposed credentials: Protect Base64 text as if it were plain-text config. If a subscription link has been exposed, remove the post and reset the link or related credentials in your provider’s account dashboard.

A successful conversion means more than “the file opens”: the target core validates it, key fields are preserved, policy references are complete, and traffic uses the expected outbound route. Prefer the target format generated by the provider. If conversion is necessary, keep the source file, record versions, and validate each layer to reduce repeated troubleshooting after subscription updates.

Download Clash