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

Learn how base64 subscription lists, native JSON configs, and vmess:// share links differ, how clients recognize them, and when conversion works.

At a glance

A subscription URL is an entry point; base64 is an encoding; a share link describes one server; and native JSON may contain an entire runtime configuration. This guide is for anyone who sees “update succeeded, but no servers appeared,” can’t import JSON, or needs to switch clients. First identify the response, then choose the right import method, and finally determine which fields can be converted and which settings must be preserved separately.

1. Distinguish the subscription URL, its response, and the node protocol

A subscription link is typically a URL that retrieves content over HTTPS. The client requests it, receives a server list or configuration text, and attempts to parse the response. The URL may contain an access token; it is not itself a VMess or VLESS protocol, and its suffix alone won’t tell you the response format. The same endpoint may also return different content depending on request parameters or the client identifier.

A typical server list consists of share links, such as lines beginning with vmess:// or vless://. Multiple links can be returned as plain text, one per line, or encoded together as base64. Another possible response is JSON—but being valid JSON only means a JSON parser can read it. It doesn’t tell the client whether to treat it as a server list or a complete configuration.

FormatHow to recognize itWhat it usually containsUse this import method
Plain-text share linksEach line starts with a protocol prefixConnection details for multiple serversShare-link or clipboard import
Base64 listDecodes to multiple lines of share linksThe same content as a plain-text listSubscription update that supports this format
Native config JSONCommon top-level keys include inbounds and outboundsSettings for inbounds, outbounds, DNS, routing, and moreCustom-config or config-file import

2. Base64 changes the representation, not the protocol capabilities

Base64 represents bytes as text that’s easier to transfer; it does not provide confidentiality. A note containing Chinese characters must first be converted to UTF-8 bytes before base64 encoding. To reverse the process, decode the bytes and then interpret them as UTF-8. Calling the browser’s btoa() directly on a string containing Chinese characters may throw an error or produce incorrect results.

3 → 4
Every 3 bytes become 4 characters; the final group may include padding
2 layers
The outer list and the VMess payload inside it may each be encoded
443
The TLS example below uses this port; it isn’t a format identifier

Standard base64 commonly uses + and /, and may end with =. The URL-safe variant replaces the first two characters with - and _, and may omit padding. Some clients support these variations, but don’t assume every importer will fix spaces, truncation, or extra quotation marks automatically.

Bottom line: decode once, then choose how to import

If decoding produces multiple share links, use the link-list import option. If it produces an object with inbounds, outbounds, and routing, check for a custom-config import option. The number of encoding layers says nothing about protocol compatibility.

3. VMess share JSON and native configs use different structures

A classic VMess share link typically uses the format “vmess:// followed by base64-encoded JSON.” The example below illustrates a share-link object; it is not a working server and cannot be used directly as a core runtime configuration. The domain is reserved for examples, and port 443 is shown only to illustrate a TLS setup.

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

Here, v is a share-format marker, not a client version; ps is a remark; and add, port, and id refer to the address, port, and user identifier. net describes the transport. In this example, host and path are used for WebSocket, while sni specifies the TLS server name. These fields serve different purposes; matching values don’t make them interchangeable.

Share-link fieldTypical location in a native configConversion notes
add、portAddress and port in outbounds[].settings.vnext[]Native configs usually expect the port to be a number, not a copied string
id、scyid and security in the VMess outbound user entryField names don’t match exactly; map them according to the core’s config schema
net、pathstreamSettings and the corresponding transport settingsA field only applies when the transport type matches
tls、snistreamSettings.security and TLS settingsProtocol encryption parameters and TLS settings belong to different layers

A native JSON config may define multiple outbounds and use routing rules to select one. Exporting a single node usually won’t include DNS, routing rules, inbound listen addresses, or outbound chains. So converting native JSON to a share link usually means extracting connection details—not reversibly compressing the entire config.

4. Convert locally: handle the text first, then the config semantics

Converting a share-link list to base64, or decoding base64 back into a list, is a text-encoding operation you can do locally. The JavaScript below only defines text-processing functions; it makes no network requests. Run it in the developer tools on a blank page you created yourself. Don’t paste real subscription content into an unfamiliar website’s console or run conversion scripts from unknown sources.

function encodeUtf8Base64(text) {
  const bytes = new TextEncoder().encode(text);
  const binary = Array.from(bytes, byte =>
    String.fromCharCode(byte)
  ).join("");
  return btoa(binary);
}

function decodeUtf8Base64(input) {
  let text = input.trim()
    .replace(/\s+/g, "")
    .replace(/-/g, "+")
    .replace(/_/g, "/");

  if (!/^[A-Za-z0-9+/]*={0,2}$/.test(text)) {
    throw new Error("Content is not valid base64 text");
  }

  if (text.length % 4 === 1) {
    throw new Error("Unexpected length; check that the content was copied in full");
  }

  text += "=".repeat((4 - text.length % 4) % 4);
  const binary = atob(text);
  const bytes = Uint8Array.from(binary, char =>
    char.charCodeAt(0)
  );
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}

function packShareList(links) {
  return encodeUtf8Base64(links.join("\n"));
}

function makeClassicVmessLink(shareObject) {
  return "vmess://" + encodeUtf8Base64(JSON.stringify(shareObject));
}

// Test only a UTF-8 round trip; do not use real subscription credentials.
const sample = "Sample note\nSecond line";
console.assert(
  decodeUtf8Base64(encodeUtf8Base64(sample)) === sample
);
  1. Decode the list: Pass the response text to decodeUtf8Base64(), then check that the output is actually a list of share links, one per line. This function won’t identify protocols or repair damaged content.
  2. Encode the list: Put each complete URI in an array and pass it to packShareList(). Newlines separate entries; don’t concatenate multiple URIs without a separator.
  3. Build a VMess link: Use makeClassicVmessLink() only when the input object already matches the classic VMess share-link fields. It can’t automatically turn an arbitrary native config into a valid node.
  4. Verify the result: Import it into the client as a test entry. Check the address, port, UUID, transport, TLS, and server name one by one before testing the connection.

Converting a native config to a share link requires a semantic converter: choose which outbound to export, identify the protocol and transport, map the fields, and check whether the target share format can represent every parameter. If a field can’t be represented, stop or document what will be omitted. Don’t silently drop settings and claim the conversion succeeded.

5. Choose the right import method in three clients

Start with what you need: use a subscription for a server list that needs ongoing updates, a share link to move one node temporarily, or native JSON when you need full control over DNS, routing, and outbounds. The client interface handles importing and management; the core runs the protocols. Check compatibility for each separately.

Subscription import

Recommended

Save the subscription URL and let the client fetch the server list manually or on a schedule. Updates are convenient, but the server’s response must be a format the current importer can recognize.

Best for: Server lists that change regularly and need ongoing management.

Share-link import

Import one or more URIs from the clipboard to inspect their parameters. This usually won’t preserve the link to the original subscription for future updates.

Best for: Temporary testing, moving a single node, and troubleshooting format issues.

Custom-config import

Load native JSON using the client’s custom-config feature. The config must match the core in use; check separately how routing and DNS are handled.

Best for: Users with a complete config who need to keep advanced outbounds and routing rules.

v2rayN is for desktop; v2rayNG is an Android client that uses the Xray core; and v2flyNG is an Android client that uses the v2fly core. VMess share links are a common way to exchange nodes, but support for specific transports and extended parameters depends on the version. In particular, don’t assume v2fly can run extended configs designed for Xray.

  1. Keep the original config

    Keep your working entries and the original response before making changes. Troubleshoot in a separate subscription group; don’t clear all your servers first.

  2. Add a subscription on desktop

    In v2rayN, the usual path is “Subscription Group” → “Subscription Group Settings” → “Add.” Enter a remark and URL, save, then update from the “Subscription Group” menu. Labels may vary slightly between platforms and versions.

  3. Add a subscription on Android

    In v2rayNG, the usual path is the top-right menu “⋮” → “Subscription Group Settings” → “+”. After saving, return to the main screen and select “⋮” → “Update Subscription” to fetch the entries. Don’t enter the subscription URL as a server address.

  4. Test a single link

    In v2rayN, select clipboard import from the “Servers” menu. In v2rayNG, the usual path is “+” on the main screen → “Import from clipboard.” Test the same share link in both to distinguish a subscription-fetch problem from a node-parsing problem.

  5. Check the core

    In v2flyNG, use its subscription settings or clipboard-import option, and check whether v2fly supports the parameters in question. Import native JSON through the custom-config option provided by your version; don’t paste it into the subscription URL field.

6. Troubleshoot by stage: fetch, parse, then connect

An “update complete” message only means the update process reached its end; it doesn’t prove that every entry was parsed successfully. Likewise, seeing an entry in the list doesn’t mean its transport and TLS parameters are correct. Pinpointing the stage where the error occurred is more useful than repeatedly changing the encoding.

What you seeCheck firstWhat to do next
Subscription request failsIs the URL complete? Is the token still valid? Did the server return 401 or 403?Check your authorization with the service provider; don’t try to bypass access controls by re-encoding the content
HTTP 200, but no entriesIs the response a login page, an error JSON object, or an unsupported format?Identify the response body, then use the appropriate import method
Base64 decoding failsWas the text truncated or wrapped in quotes? Does the client support this base64 variant?Copy the original text again and check its length and the actual response content
JSON parses but won’t importAre you confusing a share object, a config file, and an API error object?Check the top-level keys and the structure expected by the client
Entry appears, but the connection failsPort, UUID, transport, TLS, SNI, and pathCompare each field with the original config, then check network reachability
Can I base64-encode a whole native JSON config to make a universal subscription?

No. Encoding doesn’t change the data structure. After decoding, it’s still a native config. If a client only accepts a list of share links, it still won’t be able to parse that config as a list. Use an import method that supports the config, or selectively extract node parameters that the target format can represent.

Why did my routing rules disappear after importing a share link?

A share link usually describes one node and doesn’t include a complete routing setup. The client’s own routing settings may also be managed separately from rules in a native config. Before migrating, save DNS, routing, inbounds, and any other outbounds separately.

Why were my manual changes reverted after updating the subscription?

Subscription entries usually follow the remote version, so an update may recreate or overwrite local entries. Save changes you need to keep in a separate config, and remember they won’t automatically track changes to the original subscription.

Bottom line: preserve every field before worrying about conversion

Use a subscription format explicitly supported by the service provider whenever possible. If you need to migrate, test the conversion with one node first and save routing and DNS separately. Process the rest only after confirming the test works.

Download v2rayNView platforms and installers