At a glance

A subscription URL retrieves content, Base64 encodes text, a share link describes one node, and native JSON describes the runtime configuration. This guide is for anyone dealing with failed imports, missing rules after conversion, or different results across clients. First identify the data format, then use the right import option, and finally check whether conversion preserves transport, TLS, DNS, and routing settings.

1. Subscription URLs, encoding, and node configs are different layers

“V2Ray subscription” isn’t a single, standardized file format. A client first requests the subscription URL, then parses the response body. Common responses contain multiple share links, either as plain lines or Base64-encoded as a whole. Some services return JSON instead. An HTTPS URL only describes how the content is retrieved; it doesn’t guarantee that a particular importer can read the response.

ItemTypical characteristicsWhat it containsWhere to import it
Subscription URLHTTPS address, possibly with an access tokenLocation of the remote contentSubscription group settings
Base64-encoded subscription bodyUsually decodes to one link per lineA set of nodes; may not include local policiesSubscription update parser
Share linkvmess:// or vless://One node and its connection parametersImport from clipboard
Native JSONKeys such as inbounds and outboundsInbound, outbound, and optional DNS and routing settingsCustom configuration import

JSON is a syntax, not a configuration type. A VMess share object with add, id, and net can pass JSON syntax checks, but it can’t run directly as a complete Xray config. Conversely, Base64-encoding a complete config doesn’t automatically turn it into a node subscription supported by a client.

2. Base64 subscriptions: decode first, then parse each line

Traditional node subscriptions often join multiple share links with newline characters, then encode the entire UTF-8 text. The client decodes the response back into text and parses each line by protocol. A VMess link may also contain another layer of Base64 within the individual link, so successfully decoding the outer subscription doesn’t mean the inner node fields are valid.

  1. Retrieve the response: Make sure you received subscription content, not a login page, access restriction notice, or server error page.
  2. Check the outer layer: If the response already starts with a share link, treat it as plain text. Don’t decode it again blindly.
  3. Decode the content: After decoding the outer Base64 layer, check that the result contains one link per line.
  4. Parse the nodes: Identify the protocol on each line, then check the server address, port, user ID, and transport parameters.

Standard Base64 commonly uses + and /, and may end with = padding. Some sources use the URL-safe variant with - and _, or omit the trailing padding. Importers vary in how tolerant they are: missing equals signs alone don’t mean a subscription is corrupted, and Base64 should not be treated as URL percent-encoding.

// Local JavaScript example: input is the retrieved text containing multiple share links.
// TextEncoder / TextDecoder preserve Chinese node names.
function encodeSubscription(text) {
  const bytes = new TextEncoder().encode(text);
  let binary = "";
  for (const byte of bytes) {
    binary += String.fromCharCode(byte);
  }
  return btoa(binary);
}

function decodeSubscription(encoded) {
  let normalized = encoded.replace(/\s/g, "")
    .replace(/-/g, "+")
    .replace(/_/g, "/");
  normalized += "=".repeat((4 - normalized.length % 4) % 4);
  const binary = atob(normalized);
  const bytes = Uint8Array.from(binary, char => char.charCodeAt(0));
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}

This code converts text only; it doesn’t fetch subscriptions or check whether nodes can connect. If decoding fails, first check whether the content was truncated or copied along with explanatory text. Even after successful decoding, a client that supports the relevant protocol must parse the result. Calling btoa() directly on Chinese text in a browser may throw an error, so this example converts the text to UTF-8 bytes first.

3. VMess and VLESS share links: map the fields

A common vmess:// share-link format appends a Base64-encoded JSON object to the protocol prefix. The example below shows the object before encoding. It follows a common sharing convention and is not a complete core config. The domain and UUID illustrate the structure only; they aren’t usable node details.

{
  "v": "2",
  "ps": "Example VMess WS",
  "add": "node.example.org",
  "port": "443",
  "id": "2d1e7e18-49f4-4ab7-8b63-c929d11c5e29",
  "aid": "0",
  "scy": "auto",
  "net": "ws",
  "type": "none",
  "host": "node.example.org",
  "path": "/proxy",
  "tls": "tls",
  "sni": "node.example.org"
}

v identifies the share-object format, not the client version. ps is the remark; add and port specify the connection target; and net, host, and path describe the transport. The aid value here is zero; don’t infer that VLESS has a parameter with the same name. Share-link implementations may omit fields, so check the node editor after importing.

VLESS share links commonly use a URI structure: the user ID comes before @, the server and port follow it, query parameters describe transport and security settings, and the remark comes after #. The example below uses the same parameters as the JSON example later in this guide.

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

Takeaway: convert the fields, not the protocol name

Changing vmess:// to vless:// doesn’t convert the protocol. You can create a link for another protocol only if the server provides the corresponding parameters. When representing the same node in another format, preserve its transport, security, and authentication fields.

4. Native JSON: runtime settings beyond node parameters

A native Xray config describes how the entire process runs. Inbounds define where local applications connect; outbounds define how requests reach servers; routing and DNS control traffic selection and name resolution. The minimal config below illustrates the fields and uses VLESS, WebSocket, and TLS. Its example address does not provide a working service.

{
  "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.org",
            "port": 443,
            "users": [
              {
                "id": "2d1e7e18-49f4-4ab7-8b63-c929d11c5e29",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "serverName": "node.example.org"
        },
        "wsSettings": {
          "path": "/proxy",
          "headers": {
            "Host": "node.example.org"
          }
        }
      }
    }
  ]
}
10808
Example local SOCKS inbound port
443
Example remote TLS server port

These ports serve different purposes. 10808 binds to the local loopback address; it’s only an example, not a setting used on every device. 443 is the remote listening port and must match the server. If another process is already listening on the same local address and port, a second config may fail to start.

5. v2rayN and v2rayNG: choose the right import option

Menu labels can vary by client version and interface language. The steps below use common menu names. Before importing, identify whether the clipboard contains a subscription URL, a single-node link, or a complete JSON config. In particular, don’t import an HTTPS subscription URL as a regular node link.

v2rayN: import subscription groups and individual nodes separately

  1. Add a subscription group

    In the main window, go to “Subscription Group” → “Subscription Group Settings.” Select “Add,” enter a remark and subscription URL, then save. Keep all query parameters in the URL intact.

  2. Update subscriptions

    Return to the “Subscription Group” menu and choose to update the current subscription or all subscriptions. If the menu offers updates through or without a proxy, choose based on what your current network can reach, then check the update log.

  3. Import a single node

    Copy the complete vmess:// or vless:// link, then in the main window choose “Server” → “Import bulk URL from clipboard.” The common shortcut is Ctrl+V.

  4. Check the connection settings

    Open the node editor and check the address, port, UUID, transport type, path, and TLS server name. A successful import means the link was parsed; it doesn’t mean the server is reachable.

v2rayNG: subscription groups and clipboard imports

6. Conversion options: choose what to preserve

“Successful conversion” can mean at least three things: the text decodes, the client imports it, and the connection behaves as expected. Even when the first two succeed, routing, DNS, fingerprints, or extended transport parameters may be lost. Decide what needs to move first; that usually saves more troubleshooting time than hunting for a conversion button.

Move a node with a share link

Recommended

Export using the client’s share or copy-link feature, then import the link in the target client. Check each transport and TLS setting afterward; migrate local routing and DNS separately.

Best for: moving a single node between devices or organizing a small number of connections

Use a subscription URL to maintain a node list

Add the subscription URL in the target client and let it parse and update the list. Converting the response into a static list of links only captures a snapshot; it won’t keep the remote update mechanism.

Best for: groups whose nodes change frequently and need ongoing updates

Preserve policies with native JSON

Move configs between compatible cores, checking file references, ports, system permissions, and supported fields one by one. After importing a custom config, some client UI options may not apply to it.

Best for: configs with multiple outbounds, custom DNS, or complex routing

After migration, check the logs for config parsing or listener errors, then test an actual connection with a node. On desktop, also make sure the system proxy or app-level proxy points to the correct inbound. On Android, confirm the connection service has started and grant VPN interface permission when prompted. A speed test result and browser traffic routed through the proxy are two different checks.

7. Import and conversion FAQs

Why does a subscription URL open, but updating it finds no nodes?

First check whether the response body is an HTML page, an error message, or a format the client doesn’t support. A URL opening in a browser doesn’t mean it returns a node list. Make sure the token hasn’t expired, then check the client’s update log for parsing errors.

After decoding Base64, the text still starts with vmess. Do I need to decode it again?

The outer subscription layer is already decoded. In common formats, the content after the vmess:// prefix on an individual line has another encoding layer. Parse only the payload after the protocol prefix; don’t decode the entire multiline list again. The client handles this layer during a normal import.

Why won’t the core start even though the JSON check passes?

Valid syntax doesn’t guarantee valid fields. First distinguish a VMess share object from a complete config, then check whether the core supports the protocol and fields. If the log reports a listener error, check whether a local port is already in use instead of repeatedly changing the subscription encoding.

Why did my routing rules disappear after converting to a link?

Single-node links usually carry only connection parameters. To restore traffic routing, recreate routing and DNS in the target client or migrate a compatible complete config. Missing local policies don’t mean node authentication parameters were lost.

What should I do if the import succeeds but TLS or handshake errors appear?

Open the node editor and compare the port, transport type, SNI, WebSocket Host, and path against the server details. Check that the device clock is correct. Don’t turn off certificate verification to compensate for incorrect fields. If the original link also fails, continue troubleshooting the server and network connection.