Subscription Link Formats Explained: Base64 Subscriptions, Native JSON, and Share Links

Learn how Base64 subscriptions, Xray native JSON, and vmess:// and vless:// share links differ, which formats each client supports, and how to convert between them.

At a glance

A subscription URL is an access point, Base64 is a text encoding, a share link usually describes one node, and Xray native JSON defines a runtime configuration. If a link opens but fails to import, or JSON won’t add as a subscription, this guide walks through identifying the format, choosing the right import option, decoding offline, and checking fields. It also explains which conversions are straightforward and which require additional configuration.

1. Distinguish the subscription URL, response body, and node configuration

What you copy to the clipboard isn’t necessarily a node. A subscription URL beginning with https:// points to a resource; the actual response body arrives only after the client requests that URL. The body might contain share links, one per line; those lines encoded as Base64; or a different kind of configuration document. The URL suffix alone doesn’t tell you which format it is.

How to identify the four content types
ContentTypical appearanceWhat it does
Subscription URLStarts with https://, possibly with an access tokenFetches and updates a remote list; doesn’t specify the node protocol itself
Base64 subscription bodyA long string of printable characters, sometimes ending in =Usually contains multiple share links; decode it to inspect them
Single-node share linkvmess://, vless://, trojan://, or ss://Describes the server, authentication, and some transport settings
Xray native JSONCommon top-level fields include inbounds, outbounds, and routingDefines the core’s listeners, outbound connections, and traffic rules

Base64 does not provide encryption. Anyone with the body can usually recover node addresses and authentication fields; a token in a subscription URL may grant similar access. When troubleshooting, don’t post full subscription URLs, QR codes, or exported node data in public forums. In screenshots, redact the path, query parameters, and node credentials.

2. Base64 subscriptions and VMess encoding layers

A common legacy subscription format joins multiple share links with newline characters, then Base64-encodes the entire UTF-8 text. After one decode, you should see one node per line. Lines may end in LF or CRLF. Importers usually ignore blank lines, but don’t assume every version handles extra spaces or a byte order mark.

  1. Outer layer: The entire subscription body is Base64-encoded; decoding it produces multiple URI lines.
  2. Node layer: The part after vmess:// is commonly Base64-encoded too; decoding it produces a JSON node description.
  3. Field layer: Notes, paths, and query parameters in URIs such as VLESS may use percent encoding. For example, %20 represents a space.

A VMess node may therefore require two Base64 decodes, but each layer contains a different kind of data. Don’t keep decoding a long string just because it looks encoded. With vless://, the URI body should usually be parsed as a URI, not treated as another Base64 string.

Common VMess share-link JSON uses fields such as v, ps, add, port, id, net, and tls. The common value "2" for v identifies the share format version; it is not the client or core version. This JSON is not a native configuration that Xray can run directly.

3. Which fields can a share link represent?

The VLESS URI below uses a reserved example domain and an all-zero UUID. It illustrates the format and cannot establish a real connection. Port 443 is the example remote port, not the client’s local listening port.

vless://[email protected]:443?encryption=none&security=tls&type=tcp&sni=node.example.invalid#TLS%20example
Mapping VLESS URI components to Xray configuration fields
URI componentConfiguration fieldWhat to check
Host and portsettings.vnext[].address、portConvert the port to a number; don’t include any note text
UUID in user informationsettings.vnext[].users[].idMust match the server configuration
encryption=noneusers[].encryptionDoesn’t mean the transport layer has no TLS
security=tlsstreamSettings.securityDistinguish it from transport security options such as TLS and REALITY
snitlsSettings.serverNameUsed as the TLS server name; don’t change it arbitrarily
Fragment after #Client-side node labelUsually has no effect on authentication or the network connection

When converting complex transports, also check flow, alpn, the transport path, HTTP Host, and the relevant security settings. Parameter names can vary across share formats and client versions. Matching addresses, ports, and UUIDs don’t guarantee equivalent configurations.

Conversion check: verify transport settings after import

Compare the original link with the node editor field by field. Check the transport type, TLS server name, and flow before testing the connection. Re-encoding the subscription body won’t restore missing fields.

4. Xray native JSON is not a node list

Xray native JSON describes the runtime environment. inbounds determines how local traffic is received, outbounds defines how remote connections are made, and routing can direct different requests to different outbounds. The minimal example below corresponds to the VLESS node in the previous section and sets up a local SOCKS listener at 127.0.0.1:10808. All remote settings remain nonfunctional demo values.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "local-socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "node.example.invalid",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-0000-0000-000000000000",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "serverName": "node.example.invalid"
        }
      }
    }
  ]
}

This example doesn’t explicitly configure routing or dns; it only shows a single inbound and outbound. The listener is restricted to the loopback address and isn’t exposed to the LAN. udp: true allows the SOCKS inbound to handle relevant requests, but doesn’t guarantee that the app, remote server, and network path all support working UDP connectivity.

Conversion boundaries: extract a node or preserve the runtime configuration

Share link to native JSON
  • Parse the address, port, authentication, and transport settings.
  • Add a local inbound and listening port.
  • Configure DNS, logging, and routing for your needs.
Native JSON to share link
  • Choose a proxy outbound that can be represented as a URI.
  • Check whether the share format supports all transport fields.
  • Keep a separate backup of inbound, routing, and DNS settings.

A single-node URI usually can’t represent an entire runtime configuration. Don’t assume that multiple outbounds, chained connections, or routing rules will move along with the node.

5. Choosing an import method for v2rayN, v2rayNG, and v2flyNG

Being able to parse a format and having a core that can run its protocol are two separate requirements. v2rayN is a desktop client, v2rayNG uses the Xray core, and v2flyNG uses the v2fly core. Before importing, check the client’s import option, core type, and required transport support. Don’t assume that a configuration saved by one client will also run in another.

Subscription URL: keep a node list up to date

Recommended

In v2rayN, go to “Subscription Group” → “Subscription Group Settings” to add the URL, then choose “Update All Subscriptions (without proxy)” or the equivalent update-through-proxy option. Menu labels may vary slightly by version; follow the options shown in your client.

Best for: Nodes change over time and you want to keep the option to update them.

Share link: import a single node

In v2rayN, choose “Servers” → “Import bulk URL from clipboard”; the common shortcut is Ctrl+V. In v2rayNG, use “Import from clipboard” from the add menu. In v2flyNG, use its share-link import option and confirm that the core supports the protocol and transport.

Best for: Quick tests, moving one node, and checking settings individually.

Native JSON: preserve the full runtime configuration

Use the client’s custom configuration import option, not the subscription URL field. In v2rayNG, use the custom configuration import feature; in v2rayN, use the custom configuration server option. v2flyNG requires a configuration compatible with its v2fly core, so don’t use Xray-specific fields as-is.

Best for: You need fixed inbounds, DNS, or complex routing and can maintain the configuration yourself.

Decoding a subscription and importing it manually usually creates a snapshot of the nodes, without preserving the original subscription’s update link. Custom JSON also isn’t the same as a regular node managed through the UI: the port used by the system proxy must match an actual inbound. The example above listens only on SOCKS 10808; don’t assume HTTP is also available on 10809.

6. How to decode and re-encode offline

If you only need to inspect the encoding, you can use the browser developer console on a blank local page instead of submitting your subscription to an online conversion service. The steps below use built-in browser APIs and make no network requests. Start with fixed test text to confirm that TextEncoder and TextDecoder are supported, then process the retrieved body in a trusted local environment. Don’t paste a real subscription into an unfamiliar webpage’s console.

function decodeBase64Text(input) {
  const normalized = input.trim()
    .replace(/\s+/g, "")
    .replace(/-/g, "+")
    .replace(/_/g, "/");
  if (normalized.length % 4 === 1) {
    throw new Error("Invalid Base64 length");
  }
  const padded = normalized
    + "=".repeat((4 - normalized.length % 4) % 4);
  const bytes = Uint8Array.from(
    atob(padded),
    character => character.charCodeAt(0)
  );
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}

function encodeBase64Text(text) {
  const bytes = new TextEncoder().encode(text);
  const parts = [];
  for (const byte of bytes) {
    parts.push(String.fromCharCode(byte));
  }
  return btoa(parts.join(""));
}

decodeBase64Text("5rWL6K+V"); // test
encodeBase64Text("test");     // 5rWL6K+V
  1. Identify the format first: If the body already contains one URI per line, you don’t need to decode an outer layer. If it starts with a JSON object, inspect its field structure first.
  2. Decode only once: Run the decode function on a body confirmed to be Base64, then check that the result contains node lines rather than an error page.
  3. Preserve the original node text: Don’t repeatedly percent-decode an entire URI. Reserved characters in paths may change meaning as a result.
  4. Clean up the lines: Remove blank lines, but preserve the characters in each share link. When re-encoding, join the nodes with newline characters.
  5. Verify the import: Test with a single node first, then process the full list. Re-encoding is the right fix only when the encoding itself is wrong.

These functions only encode and decode; they won’t convert VMess to VLESS or automatically turn native JSON into a subscription. Protocol conversion depends on whether the server provides the corresponding endpoint—it can’t be done by changing a string prefix. Likewise, Base64-encoding native JSON doesn’t turn it into a typical subscription of one node URI per line.

7. Troubleshooting import failures and missing fields after conversion

The subscription URL opens. Why does the client show no nodes?

Check the actual response body, not just the status code. If it contains a login form, an access restriction message, or an HTML page, fix the subscription access permissions first. If it’s native JSON, use the custom configuration import option instead of repeatedly adding it in the subscription field.

It still says vmess:// after decoding. Did decoding fail?

That usually means the outer subscription decoded correctly. Try importing the complete VMess link into the client. Decode the node description after it only if you need to inspect its fields, and don’t mistake the resulting share-link JSON for a complete core configuration.

The node imported successfully. Why can’t it connect?

Open the node editor and check the remote port, UUID, transport type, TLS server name, and flow, then review the core logs. A successful import only means the parser accepted the data; it doesn’t prove the remote server is reachable, authentication is valid, or the app is using the local proxy.

Why did my routing rules and DNS disappear after conversion?

Single-node links usually contain only proxy outbound settings. Return to the original JSON backup and migrate routing, dns, and any required inbounds separately. Adding rules to a node remark won’t restore them; clients don’t recover full configurations that way.

Subscription updates time out through the proxy too. Should I change the format first?

First check whether the request completes at all. If you have a working node, try the corresponding update-through-proxy option under “Subscription Group” in v2rayN. If you don’t have a working connection, check the local network and subscription access permissions first. A request timeout happens before the response is parsed; changing the Base64 encoding won’t fix it.

Verify the result in three steps: the subscription was retrieved successfully, the node parameters were parsed completely, and traffic reaches the intended outbound. Keep the original backup before changing formats. For custom JSON, confirm that the listening address and port haven’t changed, then verify the app’s proxy settings.

Download v2rayN