V2Ray Subscription Formats: Base64, Native JSON, and Share Links

Learn how Base64 subscriptions, native JSON configs, and vmess:// or vless:// share links differ—and how to convert between them manually or with a client.

At a glance

A subscription URL retrieves content, Base64 encodes text, share links describe individual nodes, and native JSON defines a core’s runtime configuration. This guide is for anyone whose subscription has content but won’t import, or who can’t turn JSON into a share link. Identify the input first, decode it offline, then use a client to generate a config and verify its fields—so routing, DNS, and security settings aren’t lost in conversion.

1. Tell subscription URLs, subscription content, and node configs apart

A subscription URL starting with https:// is just an access point; it doesn’t guarantee any particular response format. The server might return Base64 text, newline-separated share links, or a config intended for a specific client. To determine whether it can be imported, inspect the downloaded content—not whether the URL ends in .txt or .json.

A common Base64 subscription contains multiple node share links joined by newlines, with the entire text then encoded. This can involve two layers of encoding: the outer layer covers the full list, while the content after each vmess:// prefix may contain encoded node JSON. Decoding the outer layer does not produce a complete core config. Base64 is reversible encoding, not encryption.

ItemTypical appearanceWhat it containsHow to use it
Subscription URLStarts with https://Remote resource location, possibly with an access tokenSubscription group settings
Base64 subscription contentLong encoded textUsually encoded, newline-separated share linksUpdate subscription or decode offline first
List of share linksvmess://、vless://Connection parameters for one or more nodesImport from clipboard
Native JSONContains keys such as inbounds and outboundsInbound and outbound settings, plus optional DNS and routingCustom config option

Check the content first, then choose an import method

If the content is already a list of share links, don’t decode it again. If it contains complete inbound and outbound sections, don’t add it as a subscription URL. If the response is a login or error page, fix subscription access before trying to convert anything.

2. How VMess and VLESS share links represent parameters

A common vmess:// share link encodes a JSON description of one node after the protocol prefix. Decoded fields often include add, port, id, net, host, path, and tls. This JSON is part of the share-link format, not native JSON that a core can run directly.

Here’s a VMess node description to illustrate the format. The address and UUID are examples, not credentials for a working service. v: "2" identifies the share-link format version, not the client version. Modern VMess nodes typically use aid: "0"; use the value specified by the server config.

{
  "v": "2",
  "ps": "Example-VMess-WS",
  "add": "example.com",
  "port": "443",
  "id": "123e4567-e89b-42d3-a456-426614174000",
  "aid": "0",
  "scy": "auto",
  "net": "ws",
  "type": "none",
  "host": "example.com",
  "path": "/proxy",
  "tls": "tls",
  "sni": "example.com"
}

VLESS share links typically use a URI structure. The username field holds the UUID, the host and port identify the connection target, and query parameters describe the transport and security layer. The text after the hash is the node label. This example uses WebSocket and TLS, with no additional flow-control parameters.

vless://[email protected]:443?encryption=none&security=tls&type=ws&host=example.com&path=%2Fproxy&sni=example.com#Example-VLESS-WS

3. Decode and re-encode Base64 subscriptions offline

Treat subscription URLs, UUIDs, and decoded content as sensitive data. Don’t submit them to unfamiliar online conversion tools. You can run reviewed code in the developer console of a blank local page; first make sure no other scripts on the page can read your input, and remember that developer tools may retain history.

These functions convert strings locally and make no network requests. The decoder supports standard Base64 and URL-safe characters, tolerates line breaks, and restores missing trailing padding. If the input isn’t Base64, check its source rather than repeatedly encoding and decoding it in the hope of getting a result.

function decodeBase64Utf8(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.padEnd(
    Math.ceil(normalized.length / 4) * 4, "="
  );
  const bytes = Uint8Array.from(atob(padded), c => c.charCodeAt(0));
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}

function encodeBase64Utf8(text) {
  const bytes = new TextEncoder().encode(text);
  let binary = "";
  for (const byte of bytes) binary += String.fromCharCode(byte);
  return btoa(binary);
}
3 → 4
Base64 converts every 3 bytes into 4 characters
443
Remote TLS port used in this example
10808
Local SOCKS port used in this JSON example

Pass the raw subscription content as a string to decodeBase64Utf8, then check whether the result contains protocol prefixes on separate lines. To create a traditional Base64 subscription, first collect valid share links, then call encodeBase64Utf8(links.join("\n")). Don’t URL-decode the entire list; that could prematurely decode separators that are already encoded in node parameters.

  1. Remove any byte-order mark at the start of the text, split on \r?\n, and remove blank lines.
  2. Check the protocol prefix on each line. Don’t include error pages, traffic notices, or announcements in the node list.
  3. Only decode the part after vmess:// in an individual link if you need to inspect its VMess node fields.
  4. After re-encoding, decode it again to confirm the number of node lines and any non-ASCII labels are unchanged, then test the import in a client.

4. Import, export, and generate configs with a client

For everyday migrations, let a client parse share links and generate the corresponding core config. This reduces hand-written field errors, but you still need to verify the transport parameters. Menu labels in desktop versions of v2rayN can vary slightly by version; the steps below use common names. If you can’t find an exact match, look for the equivalent function rather than switching to an unrelated custom-config option.

  1. Back up your groups

    Note the active server and subscription groups, and save the original subscription URL. Put test content in a separate group to avoid overwriting your usual setup during a bulk import.

  2. Add a subscription

    In v2rayN, open “Subscription group” → “Subscription group settings,” add a group, and enter the subscription URL. Go back to “Subscription group” and select “Update all subscriptions (no proxy).” If the URL requires a proxy, choose the corresponding update option that uses one.

  3. Import a single link

    Copy the complete share link, then import it using “Server” → “Import batch URLs from clipboard.” Paste node links here only; use the custom-config feature for local JSON files.

  4. Export and verify

    Select a node, right-click it, and use the share-URL export or copy option available in your version. Import the result into a test group, open the editor, and compare the address, port, UUID, transport type, SNI, and path.

  5. Test the connection

    Set the test node as the active server, check the core startup log, then verify a real request. Export native JSON only if the client provides an “Export full config” option or equivalent; copying a share link is not the same as exporting a runtime config.

In v2rayNG for Android, the common workflow is “+” → “Import from clipboard” on the main screen. Add subscription URLs in “Subscription group settings,” then update the subscription. In v2flyNG, distinguish subscription management from importing a single node as well. The two Android clients use different core families. A VLESS link being parsed doesn’t guarantee support for all its required transports; check features such as REALITY in particular.

5. Map share-link fields to native JSON

The target core—not the file extension—determines the native JSON structure. A complete runtime config needs a local inbound as well as the remote node and any required outbound selection. The Xray-style VLESS, WebSocket, and TLS example below shows how fields map from the URI above. The example address is not a working service. If another program is already using 10808 on your machine, choose a free port and update the client that connects to it.

{
  "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": "example.com",
            "port": 443,
            "users": [
              {
                "id": "123e4567-e89b-42d3-a456-426614174000",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "serverName": "example.com",
          "allowInsecure": false
        },
        "wsSettings": {
          "path": "/proxy",
          "headers": {
            "Host": "example.com"
          }
        }
      }
    }
  ]
}

For VMess, generate the outbound protocol and user fields using the VMess structure; changing only protocol to vmess doesn’t complete the conversion. VLESS links using REALITY, a specific fingerprint, or flow control also require the corresponding structures supported by the target core, with required parameters such as the public key and short ID preserved. Valid JSON syntax only confirms that the format is valid; it doesn’t confirm core support or correct server parameters.

6. What conversions preserve—and what they don’t

“Convert JSON to a subscription” is often mistaken for simply Base64-encoding a file. It takes at least two steps: identify shareable proxy outbounds in the config and convert them to URIs understood by the client, then package those URIs in the format expected by the subscription consumer. Base64-encoding the full JSON directly leaves you with JSON when decoded; it won’t magically become a node list.

Export share links from the client

Recommended

Export entries the client has already recognized as standard nodes to reduce field-mapping errors. After export, still check the security layer, transport parameters, and compatibility with the target client.

Best for: moving a single node or organizing a personal node list

Convert node fields offline

Handle only protocols and parameters you know are supported. Stop and record any unrecognized fields. Don’t silently discard flow control, SNI, or transport settings just to make an import succeed.

Best for: checking a small number of nodes or troubleshooting encoding and URI escaping

Keep the complete native config

Preserve inbound settings, DNS, routing, and relationships between multiple outbounds by running the config as a custom setup in a compatible core. When migrating between cores, check each field individually.

Best for: complex routing, chained outbounds, or configs that depend on the full runtime environment

Native configs may include direct and blocking outbounds, load balancing, and domain-based routing rules. These can’t be represented in a single-node URI. Even if you extract every proxy server, the original rules for which outbound handles each request won’t carry over with the share links. A converted node may connect successfully without restoring the original routing policy.

Migrate nodes and configs separately

If you only need to connect to the same server, export a share link and verify every field. If you need to preserve DNS, routing, or relationships between multiple outbounds, back up the full config and restore the runtime policies separately in the target client.

7. Subscription conversion FAQs and troubleshooting

Why is my node list still empty after a successful subscription update?

First check whether the response content actually contains nodes. If the decoded result starts with HTML tags, it’s usually a login or access-error page. If it’s native JSON, use the appropriate config import option. Then check the group filters in case the nodes were imported but are hidden in the current view.

Why are non-ASCII labels garbled after decoding Base64?

Decode the bytes as UTF-8; don’t treat the byte string returned by atob as text. You can use the TextDecoder method from this guide. If only the URI label after the hash still contains percent-encoded text, decode just that label.

Why does a link converted from JSON import but fail to connect?

Check the address, port, and UUID first, then verify the TLS SNI and the WebSocket Host and path. If the original config uses REALITY or another specialized feature, confirm that the target core supports it and that all required parameters are present. Don’t hide missing conversion fields by disabling certificate verification.

Why are my direct-connection rules for mainland China gone?

Share links usually contain information for a single node, not the full routing config. Restore your routing policy in the target client’s routing settings, or use a compatible full native config. Check the default outbound; don’t mistake a direct outbound for a proxy node that needs to be exported.

What should I do after sharing my subscription URL with a conversion website?

If you can’t confirm how the site stores your input, reset the subscription token in your provider’s management panel, then update the URL in your client. Resetting the token may not change existing node credentials. If those links were exposed too, rotate the relevant credentials if your provider allows it.

Verify everything in this order: correct input type → correct decoded text → complete node fields → compatible core features → successful real request. Change one thing at a time and keep a copy of the original text. That way, when an import fails, a TLS handshake errors, or routing changes, you can pinpoint the missing field instead of repeatedly switching formats.

Download client