Clash Subscription Formats: Convert YAML Configs, Base64 Subscriptions, and URI Links

Compare YAML configs, Base64 subscriptions, and proxy URIs. Learn how fields map, what gets lost during conversion, and how to run your own converter safely.

Identify the content before choosing an import method

Use the Clash or mihomo subscription URL explicitly provided by your service. A complete YAML config can usually be added directly to the client's config list. For Base64 text or a set of protocol URIs, first check whether your client supports that format. Don't just change the file extension, and don't assume that a successful download means the core accepted the config.

First, distinguish two easily confused things: a subscription URL is the endpoint used to fetch content; the subscription format is the structure of the data it returns. The same HTTPS URL may return different content depending on query parameters or request headers. A URL ending in .yaml is not guaranteed to return YAML, and a URL with no extension can still return a complete config.

FormatTypical contentUsually includesCheck before importing
Clash YAMLContains keys such as proxies, proxy-groups, and rulesProxies, proxy groups, and rules; may also include DNS and port settingsDistinguish a complete config from a proxy list
Base64 subscriptionA long block of encoded text that often decodes into multiple lines of URIsDepends on the original content; often contains proxy details onlyDecode it first, then identify the content format
URI linksStarts with prefixes such as ss://, vmess://, or trojan://A single proxy's protocol, address, authentication, and transport parametersConfirm the importer supports the protocol and its parameters

YAML configs: distinguish complete configs from proxy lists

Start with the top-level fields. YAML is a data format, not a Clash-specific format. Being readable by a YAML parser doesn't mean the data follows the Clash config structure; matching one core's structure doesn't mean another core supports every protocol or option in it.

Identify three common types of YAML content

  • Complete config: Usually includes proxies, proxy-groups, and rules, and may also configure listening ports, DNS, and TUN.
  • Proxy list: Contains only proxies. It can be used with a compatible proxy provider, but that doesn't mean proxy groups or routing rules are already configured.
  • Rule set: Often uses the top-level field payload. It's intended for use by rule-providers, not as a proxy subscription, and can't be imported as a proxy list.

The config below illustrates the structure. edge.example.com and demo-password are demonstration values and won't work for a real connection. The example sets the local mixed port to 7890, binds only to the loopback address, and routes all matching traffic through a manual selection group.

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

proxies:
  - name: DemoTLS
    type: trojan
    server: edge.example.com
    port: 443
    password: "demo-password"
    sni: edge.example.com
    udp: true

proxy-groups:
  - name: Manual
    type: select
    proxies:
      - DemoTLS
      - DIRECT

rules:
  - MATCH,Manual

Here, 443 is the remote proxy server port, while 7890 is the local proxy port that apps connect to. They aren't interchangeable. When converting YAML to a proxy URI, keep the remote port. Local listening ports, proxy groups, and fallback rules usually aren't included in proxy links.

Choose a config that matches the core you're running

Check the core name in the client's About or core information screen, then review the protocols it supports. A client interface that accepts pasted links doesn't mean its active core supports the protocol in those links. mihomo, which continues the development of Clash Meta, and earlier Clash cores differ in their protocol and field support. Choose the conversion target based on core compatibility, not just the file name.

Base64 subscriptions: decoding isn't conversion

Decode the content locally and inspect the result first. Base64 is an encoding method, not a proxy protocol or encryption. A common subscription format encodes multiple lines of proxy URIs as one block of text. Some services encode other structures, so don't assume the decoded content can be imported directly into Clash.

Watch for two layers of encoding: a subscription may use Base64 on the outside, while an individual vmess:// link inside may encode JSON as Base64; some ss:// links also encode authentication details. First decode the outer subscription, then pass each link to a protocol-aware parser. Don't repeatedly decode every string.

Inspect encoded content locally with Python

Save the encoded body you received as subscription.txt, then run the following code with Python 3. The script handles common Base64 and URL-safe alphabets, ignores whitespace, and restores missing padding. It writes the output to decoded.txt and makes no network requests. If the input is actually YAML, a web page, or plain URIs, handle it according to its real format instead.

from pathlib import Path
import base64
import binascii

source = Path("subscription.txt").read_text(encoding="utf-8")
compact = "".join(source.split())
compact += "=" * (-len(compact) % 4)

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

Path("decoded.txt").write_text(text, encoding="utf-8")
print("Wrote decoded.txt; inspect the content type locally.")
  • If you see lines beginning with ss://, trojan://, or similar, map the URI fields into a config next.
  • If you see proxies:, check whether the content is already YAML. Don't run a "proxy conversion" on it again.
  • If you see an HTML login page, an error message, or a JSON error object, fix the subscription request first. A converter can't generate proxy data that isn't there.

Conversely, Base64-encoding multiple lines of URIs simply repackages the same links. It won't generate rules or proxy-groups, or restore missing TLS, WebSocket, or authentication parameters.

URI to YAML: map connection parameters field by field

Verify the fields with a single proxy before converting in bulk. The Trojan URI below corresponds to the YAML proxy shown earlier. The fragment #DemoTLS is a display name, not authentication data; the sni query parameter specifies the TLS server name.

trojan://[email protected]:443?sni=edge.example.com#DemoTLS
URI componentYAML fieldWhat to do
trojan://type: trojanMap the protocol as-is; don't substitute another proxy type
demo-passwordpasswordDecode percent-encoding according to URI rules
edge.example.comserverKeep the connection address; don't replace it with a remark
443portUse the remote port number, not the local proxy port
sni=edge.example.comsniStore separately from server; the values may differ
#DemoTLSnameRestore the name, then check for duplicates and group references

This URI doesn't explicitly include udp: true from the earlier example, so it can't reproduce the original config exactly. Converters may apply their own defaults. Before using the result, explicitly verify the UDP setting against the service's capabilities and the target core's requirements.

Handle the protocol and transport separately

  • Shadowsocks: Preserve the cipher, password, server, and port. For plugin-enabled links, also check the plugin name and options. Saving only the host and password may not be enough to connect.
  • VMess: Common share links contain encoded JSON. Parse fields such as UUID, port, transport type, and TLS settings; don't treat the entire decoded string as YAML.
  • VLESS and other protocols: First confirm support in both the target core and the converter. Then check protocol-specific parameters such as flow, TLS, and Reality. Don't mechanically substitute fields just because their names look similar.
  • WebSocket or gRPC: Preserve parameters such as the path, Host header, or service name. Even with the same proxy protocol, a mismatch in transport settings can make the connection fail completely.

Don't write a generic parser by splitting strings at colons. IPv6 addresses contain colons, passwords may contain escaped characters, and proxy names may include non-ASCII text. Use URI parsing tools and handle encoding according to each protocol's conventions. Avoid decoding twice, which can alter literal characters in passwords.

YAML to URI: know what won't carry over

Treat a URI as a way to share a single proxy node. A complete YAML config contains much more, and exporting a proxy link usually won't preserve the entire runtime setup. To switch clients while keeping your routing logic, migrate a compatible full config instead of exporting and reimporting URIs.

Check four types of settings that won't migrate with a proxy node

  1. Proxy groups: Group members, health-check URLs, and switching logic for select, url-test, and fallback groups usually aren't part of a proxy URI.
  2. Routing rules: DOMAIN-SUFFIX, GEOIP, MATCH, and rule provider references aren't automatically preserved when you export a proxy node.
  3. Local environment: Listening ports, system proxy, TUN, DNS, and external controller settings need to be configured separately in the new config or client.
  4. Extended parameters: Special transports, certificate handling, multiplexing, and core-specific fields may not have a standard URI representation, or may only be recognized by some importers.

Even when both ends support the same protocol, share-link parameter names can differ between implementations. After conversion, compare the server, port, authentication, TLS name, and transport settings field by field. Don't delete unrecognized parameters just to get an import to succeed, and don't treat skipping certificate verification as a universal fix.

Build your own converter: restrict inputs and pin the output template

Prefer running it locally or deploying it in an environment only you can access. A self-hosted converter isn't just about generating YAML: it also needs to protect source subscription credentials, restrict remote fetches, and use a fixed template you can review.

  1. Choose an implementation: Confirm the converter explicitly supports the source protocol, target core, and required extended fields. Record the deployment and template versions, and keep a working output before updating.
  2. Restrict the listening address: A local service can listen only on 127.0.0.1. For example, if you use local port 25500, don't expose it directly to the public internet. Use the port specified by your implementation's configuration.
  3. Pin the template: Review its proxy groups, rules, and remote resource URLs. Only replace the proxy nodes during conversion; don't let an untrusted template also change DNS, controller settings, or traffic routes.
  4. Handle remote URLs safely: Use allowlists for source subscriptions and remote templates. Restrict redirects, response sizes, and request timeouts to prevent the service from accessing private networks or cloud instance metadata endpoints.
  5. Control logging: Avoid logging full query strings, authentication headers, or subscription content. Redact tokens before sharing logs, and periodically remove generated files and caches.

When passing a source subscription through a URL parameter, percent-encode the entire source URL once. For example, if it contains ?token= and &type=, embedding it unencoded in another URL may cause the outer request to split the parameters. Prefer a local file or request-body input supported by the converter's documentation to reduce the chance of credentials appearing in browser history or access logs.

Also distinguish a one-time conversion from an ongoing subscription: a generated file downloaded once is only a snapshot. For automatic updates, keep a reliable update endpoint or use a proxy provider mechanism supported by the core. Whether remote updates overwrite local changes depends on how the client merges or replaces configs; an "auto-update enabled" switch doesn't tell the whole story.

Validate the result: check structure, connectivity, and routing in stages

Check the config before switching the active one. Keep the original working subscription for comparison. With mihomo, you can test the generated file; the filename in the command below should be the output file saved locally.

mihomo -t -f ./converted.yaml

A passing test means the config passed checks by that core; it doesn't mean the remote proxies are reachable. Configs that reference remote resources may also depend on local files, network access, and the working directory. Keep the test environment as close as possible to the production environment.

SymptomCheck firstNext step
Subscription downloads, but no proxies appearWhether the response is a web page, error object, or unsupported encodingInspect the response locally, confirm its format, then import it
Unknown proxy type reportedWhether the core supports the protocolCheck core capabilities and the conversion target; don't just change the extension
Proxy appears but connection failsPassword, SNI, transport path, and service nameCompare each field with the original proxy and test one node first
Connection works, but routing has changedProxy groups, rule order, and the MATCH targetCheck the connection log for the matched rule and selected policy
Local rules disappear after an updateWhether local edits were made directly to a remote config copyUse the client's supported override or a separate config

Run a minimal validation

  • Compare proxy counts and names before and after conversion. Check for filtering, renaming, or parsing failures.
  • Select one known-working proxy. Confirm its TLS and transport parameters are intact before applying the changes to the full list.
  • If you use the example's 7890 mixed port, first confirm it's listening successfully, then connect your app to that port.
  • Test regular web access, DNS resolution, and apps that require UDP separately. One successful webpage doesn't verify every use case.

Finally, keep a conversion record: source format, target core, converter and template versions, and any fields added manually. If an update later breaks, you can pinpoint whether the issue is in the source data, parsing, or local runtime config, instead of repeatedly trying different online converters.

Download the latest version of Clash