Subscription formats: distinguish proxy lists from full configs

A “subscription URL” describes how to retrieve content, not the file format. The same URL may return different content depending on request parameters, headers, or server settings. To check whether a Clash client can import it, inspect the actual response rather than relying on a .yaml file extension.

Common inputs include newline-separated proxy share links, a text file containing the Base64-encoded link list, and full configs with proxies and routing rules. Base64 is an encoding, not encryption or a proxy protocol. Anyone who knows how to decode it can recover server addresses and credentials, so encoded subscriptions still need to be kept private.

Content type Typical structure Usually contains Does not guarantee
Proxy link list One link per line, such as ss://, vmess://, or vless:// Proxy address, authentication, and some transport settings Configured proxy groups, DNS, and routing rules
Base64 subscription Text containing the encoded list Usually the same as the decoded link list That a particular client recognizes every protocol
Clash YAML proxies、proxy-groups、rules Proxies, proxy groups, rules, and optional DNS settings That every Clash-family core supports every field
sing-box JSON inbounds、outbounds、route Inbound and outbound settings, routing, and optional DNS That every field maps directly to Clash YAML

Identify the content: inspect the response before decoding Base64

Rule out login and error pages first

After downloading a subscription locally, inspect the beginning of the file in a text editor. If you see <!DOCTYPE html>, a login form, or an access-denied message, you have a web page, not a config. An HTTP 200 status does not guarantee valid content: expired subscriptions, blocked requests, or accounts requiring verification may all return HTML.

  1. Make sure the subscription came from the correct client-specific link in your account page, and check that it has not expired.
  2. Inspect the final response after redirects so you don't send an error page to the converter.
  3. If the content already contains proxies: or a JSON object, identify its structure first; don't decode it again as Base64.
  4. Decode locally only if the content is actually encoded text. Never paste a subscription containing tokens into a public online decoder.

Decode locally

This Python 3 standard-library example reads a local subscription.txt, supports the common standard and URL-safe Base64 alphabets, and restores missing padding. It handles only the encoding layer; it does not parse proxy protocols. Run it in a private terminal: the output may contain passwords and should not be included in support tickets or public logs.

from pathlib import Path
import base64

raw = Path("subscription.txt").read_text(encoding="utf-8").strip()
compact = "".join(raw.split())
compact += "=" * (-len(compact) % 4)
decoded = base64.b64decode(compact, altchars=b"-_", validate=True)
print(decoded.decode("utf-8"))

A decoding error does not necessarily mean the subscription is corrupt; the content may not have been encoded in the first place. Once you have valid UTF-8 text, check whether it consists of newline-separated proxy links. Decoding the full list does not mean each link should be decoded again as a whole: Shadowsocks user info and some VMess share formats have their own encoding rules, which protocol parsers handle separately.

How conversion works: parse proxies, then generate the target config

Subscription conversion is not a matter of swapping YAML colons for JSON braces. A converter typically parses the source format, extracts the protocol, address, port, credentials, and transport settings into an internal proxy model, then outputs fields supported by the target core. Proxy groups, rules, DNS, and inbound settings are often added from a separate template.

So “conversion successful” only means that output was generated. It does not mean the proxy protocol works or that the source config's behavior was fully preserved. When exporting proxy links from a full YAML config, settings beyond the proxies—such as proxy groups, rule providers, DNS routing, and TUN—usually cannot fit in the links. Generating JSON from those links will not restore the missing information.

Common conversion paths and their limits

The original Clash core and Clash Meta (now called mihomo) support different capabilities. For configs using newer protocols, choose a target core that explicitly supports them. Renaming a file to .yaml does not give an older core support for VLESS, Reality, or other newer features. GUI clients may also bundle different core versions, so check the version actually running.

Field mapping: compare a Shadowsocks proxy

The two configs below describe the same example Shadowsocks outbound: the server port is 8388, and the local mixed-proxy port is 7890. The domain uses the reserved .invalid suffix, and the password is a demo value. These examples are for understanding field mappings only and cannot connect to the internet. Neither config enables TUN or the system proxy.

mihomo YAML: proxy, selection group, and fallback rule

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

proxies:
  - name: Example Proxy
    type: ss
    server: node.example.invalid
    port: 8388
    cipher: aes-128-gcm
    password: "demo-only-password"

proxy-groups:
  - name: Proxy Select
    type: select
    proxies:
      - Example Proxy
      - DIRECT

rules:
  - MATCH,Proxy Select

sing-box JSON: inbound, outbound, and final route

{
  "log": {
    "level": "info"
  },
  "inbounds": [
    {
      "type": "mixed",
      "tag": "mixed-in",
      "listen": "127.0.0.1",
      "listen_port": 7890
    }
  ],
  "outbounds": [
    {
      "type": "shadowsocks",
      "tag": "Example Proxy",
      "server": "node.example.invalid",
      "server_port": 8388,
      "method": "aes-128-gcm",
      "password": "demo-only-password"
    },
    {
      "type": "direct",
      "tag": "Direct"
    },
    {
      "type": "selector",
      "tag": "Proxy Select",
      "outbounds": ["Example Proxy", "Direct"],
      "default": "Example Proxy"
    }
  ],
  "route": {
    "final": "Proxy Select"
  }
}

Here, cipher maps to method, the proxy server's port maps to server_port, and the proxy name becomes the referenced tag. Clash's built-in DIRECT is represented by an explicit direct outbound in this JSON example. For a real deployment, validate the config against your sing-box version, especially for evolving areas such as DNS, TUN, and routing actions.

Two programs cannot bind to the same local 7890 listening port at the same time. For comparison tests, stop one program or change the other config to 7891, then update the proxy address in your test tool. Otherwise, “port already in use” can be mistaken for a subscription conversion failure.

Conversion options: check the target, filters, and template

Conversion services do not share a standard set of parameters. Some deployments based on subconverter accept parameters such as target, url, include, exclude, and config, but matching names do not guarantee matching behavior. Check the service provider's documentation; don't assume a conversion URL from another service will work.

Option What to check Common outcome
Target format Whether the target explicitly supports your mihomo or sing-box version An unsupported protocol or field may be dropped if you choose the wrong target
Source subscription URL Whether nested URLs are encoded as required by the service An & in the source URL is treated as a separator for outer parameters
Proxy filters Whether include and exclude filters use regular expressions Filtering removes every proxy, leaving proxy groups with broken references
Config template Whether it overrides DNS, rules, group names, or listening ports The proxies stay the same, but traffic routing and DNS behavior change
Renaming and sorting Whether names remain unique and references are updated accordingly Duplicate proxy names or dangling references cause a config load error

Pass a nested URL to a URL-encoding tool as a single parameter value; don't just replace spaces by guesswork. Avoid repeatedly encoding the entire output, or a % in the source URL may turn into %25. If the converter returns an empty list, temporarily remove the filters and check target compatibility first.

After conversion: validate the syntax, then test how it behaves

Step 1: Validate the config with the target core

Valid JSON syntax does not guarantee a valid sing-box config, and parseable YAML does not mean the core supports every field. Run the following checks in an environment with the corresponding command-line program installed; use the executable name and path for your installation.

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

Fix unknown fields, missing outbounds, and references to nonexistent proxy groups before starting the proxy. If the GUI client merges or overrides settings, inspect the final runtime config it generates as well: passing validation before overrides does not guarantee the result is still valid.

Step 2: Check connection settings that are easy to lose

Step 3: Check routing and DNS

Clash evaluates rules in order, with MATCH commonly used as the final fallback. sing-box has its own structure for routing rules, actions, and route.final; rules cannot be copied line by line. Check that each rule's target exists, and make sure proxy server hostnames do not depend on a proxy connection that has not been established yet. Otherwise, DNS resolution and connection setup can end up waiting on each other.

Don't blindly carry over DNS fake-IP settings, domain resolution strategies, rule-set formats, or download paths. Clash rule-providers and sing-box rule sets are not the same file format. Being able to read a text list does not mean the formats can share a binary rule set. TUN also involves interfaces, routes, and system permissions—details that proxy links cannot fully express.

FAQ: importing, updating, and choosing a format

Why can I see proxies after conversion but still not connect?

First rule out demo domains, expired subscriptions, and port conflicts, then check the error layer in the logs. For DNS failures, check DNS settings. For TCP connection failures, check the address, port, and local network. For TLS handshake failures, check the system clock, server name, and certificate. Repeatedly switching between Base64 and YAML usually won't fix connection-layer problems like these.

Why did my local changes disappear after updating the subscription?

A remote config update usually replaces the downloaded content. To keep DNS, rules, or proxy group changes, use the client's supported override or merge feature and confirm it still runs after subscription updates. Static files generated by a converter do not update themselves either. To keep them in sync, retain a way to fetch the source and a repeatable conversion workflow.

Which format should I use?

  1. mihomo only: Prefer YAML that the provider explicitly identifies as compatible with mihomo, and check whether it is a full config or a proxy provider.
  2. sing-box only: Prefer JSON built for your current core version, and avoid repeatedly converting a full config through other formats.
  3. Sharing proxies across multiple cores: Keep one trusted proxy source, then maintain separate rules and DNS templates for each core instead of forcing them to share one main config.
  4. Base64 link list only: Decode and convert it in a trusted environment. Record how many proxies went in, how many came out, and which protocols were skipped, then test that the config loads.

A reliable conversion should meet three requirements: no proxy settings are missing, the target core accepts the config, and traffic follows the expected rules. Keep the original subscription source and the last working config to make differences easier to trace after a template or core upgrade. For field definitions, see the YAML handbook; for basic concepts, see the quick reference.