A subscription URL handles updates, Base64 wraps text, a share link describes a single node, and native JSON configures the core. If you can fetch a subscription but can’t import it, or routing rules disappear after converting JSON to a link, start by identifying the input format. Then check the protocol, transport, and security fields before importing and testing in your client. Encoding changes the wrapper, not the configuration.
1. Subscription URLs, response content, and node formats
“V2Ray subscription” isn’t a file standard implemented consistently by every client. In practice, a subscription is usually an HTTPS URL that a client requests to retrieve nodes and save in a subscription group. The URL may contain an access token, but import compatibility depends on the response content and the formats the client can parse.
| Input type | Typical structure | What it contains | Where to use it |
|---|---|---|---|
| Subscription URL | HTTPS URL | Location of the content and access credentials | Subscription group settings |
| Base64 subscription | A single block of encoded text | Usually share links, one per line | Subscription updates or an importer that supports the format |
| Share link | vmess://、vless:// | Connection parameters for one node | Import from clipboard or QR code |
| Native JSON | Objects such as inbounds and outbounds | Inbound and outbound settings, with optional DNS and routing | Custom config option or the corresponding core |
Some subscriptions return a plain-text list of links; others serve different formats depending on request parameters or client identifiers. A .json extension or a string of gibberish in the browser doesn’t tell you whether a client can import it. Check the actual response, not just the URL. Re-encoding a login page, expiry notice, or error JSON won’t produce valid nodes.
2. Base64 subscriptions and VMess: two layers of encoding
A typical Base64 subscription is created by joining multiple share links with line breaks, encoding the UTF-8 text as Base64, and returning it as the response body. The client first decodes the outer layer, then parses each node line by line. Don’t prepend vmess:// to a block of Base64 text—that makes the whole list look like a single VMess node.
- Retrieve the response body and rule out an HTML page, an access error, or empty content.
- If the body already contains protocol-prefixed links, one per line, treat it as a plain-text link list.
- If the body is Base64, decode the outer layer and check that the result contains recognizable share links.
- Parse the nodes line by line. For
vmess://links, decode the inner payload according to the format.
A widely used VMess share format consists of vmess:// followed by a Base64-encoded JSON object. This JSON contains share-link fields; it is not a complete V2Fly or Xray config. The example below shows the decoded object inside a VMess link. The value 2 for v marks the share format version, not the core version.
{
"v": "2",
"ps": "Sample node",
"add": "example.com",
"port": "443",
"id": "0f3a10d2-7f3b-4b58-a3f6-9a6fcbec8431",
"aid": "0",
"scy": "auto",
"net": "ws",
"type": "none",
"host": "example.com",
"path": "/ws",
"tls": "tls",
"sni": "example.com"
}
The example domain, UUID, and path are for illustrating the structure only and won’t connect to a server. aid: "0" is commonly used with modern VMess AEAD; don’t change it to 64 based on outdated tutorials. scy controls VMess content encryption, while tls controls transport security. They are separate settings. Generators may also differ in how they handle numbers, strings, and extension fields.
- Outer-layer error: The full subscription won’t decode, or the decoded result isn’t a node list.
- Inner-layer error: The list decodes, but a VMess payload isn’t valid JSON.
- Character encoding error: Chinese remarks appear garbled, often because UTF-8 bytes were treated as characters directly.
3. VLESS link query parameters and transport fields
Common VLESS share links use a URI structure: the UUID goes in the user-info section, the host and port in the address, and transport, security, and path settings in query parameters. The remark follows #. The example below is a VLESS + WebSocket + TLS link; %2Fws decodes to /ws.
vless://[email protected]:443?encryption=none&security=tls&type=ws&host=example.com&path=%2Fws&sni=example.com#VLESS-WS-TLS
VMess share-link field
- Server
add = example.com- Remote port
port = 443- Transport and path
net = ws、path = /ws- Content encryption
scy = auto- Transport security
tls = tls、sni = example.com
These abbreviated fields must be mapped to the core config; they can’t be used as-is as an outbound object.
VLESS query parameter
- Server
example.com:443- Transport
type=ws- Request path
path=%2Fws- Protocol encryption field
encryption=none- Transport security
security=tls、sni=example.com
VLESS itself doesn’t provide content encryption; in this example, TLS secures the transport.
Special characters in URI parameters must be escaped correctly. For example, if a path contains &, encode it as %26; otherwise, it will be treated as a separator for the next parameter. Enclose IPv6 hosts in square brackets. Percent-encode non-ASCII remarks for better compatibility across clients. Changing the remark usually won’t affect the connection, but changing the path, SNI, or Host will affect the handshake.
4. Why native JSON can’t be used as a subscription
A native config tells the core how to run. It can include multiple inbounds and outbounds, as well as DNS, routing, and logging settings. The VMess config below illustrates the structure: a local SOCKS inbound listens on 127.0.0.1:10808, and a remote outbound connects to example.com:443. Port 10808 is an example local port, not a subscription URL port or a value used by every client.
{
"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": "vmess",
"settings": {
"vnext": [
{
"address": "example.com",
"port": 443,
"users": [
{
"id": "0f3a10d2-7f3b-4b58-a3f6-9a6fcbec8431",
"alterId": 0,
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "example.com"
},
"wsSettings": {
"path": "/ws",
"headers": {
"Host": "example.com"
}
}
}
}
]
}
Here, settings.vnext holds the VMess server parameters, while streamSettings holds transport and security settings. VLESS uses a different protocol settings structure; changing protocol from vmess to vless isn’t enough. Xray and V2Fly configs share some syntax, but their extensions and fields evolve differently. Valid JSON doesn’t guarantee that the target core can run it.
Conversion limits: migrating a node vs. a complete config
Migrate nodes only
- Extract the server, port, and UUID from the proxy outbound.
- Preserve the WebSocket path, Host, and TLS SNI.
- Generate a share link supported by the target client.
- Set DNS, routing, and local listening ports in the target client.
Migrate the complete config
- Preserve outbound tags and their routing references.
- Check DNS, inbound listeners, and logging settings.
- Check every field supported by the target core.
- Use the custom config option and verify the runtime logs.
Standard node share links don’t carry full DNS and routing rules. Converting a complete config to links and importing it back is usually a lossy round trip.
5. Import support in v2rayN, v2rayNG, and v2flyNG
The client parses subscriptions and manages nodes; the core handles the actual connection. v2rayN is a desktop client that can use supported cores depending on its configuration. v2rayNG is an Android client that uses the Xray core, while v2flyNG is an Android client that uses the V2Fly core. The fact that all three can parse VMess links doesn’t mean they support every VLESS, REALITY, or full JSON config in the same way.
| Client | Common inputs | Handling native configs | What to check |
|---|---|---|---|
| v2rayN | Subscription URLs and supported node share links | Use the custom config option with a compatible core | Whether both the importer and selected core support the node fields |
| v2rayNG | Subscription URLs, clipboard links, and QR-code nodes | Use the custom config feature available in your version | Xray version and whether transport and security parameters are complete |
| v2flyNG | Subscription URLs and supported node share links | Use the current version’s option for V2Fly configs | Don’t pass an Xray-specific config directly to V2Fly |
The steps below use common menus in the Chinese interface of v2rayN 7.x; labels may vary between minor versions. First identify whether you have a subscription URL or node text, then choose the matching import option. This works better than repeatedly trying the same button.
-
Identify the input
Use a subscription group for an HTTPS subscription URL. Import text starting with
vmess://orvless://as a node link. Treat JSON containinginboundsandoutboundsas a complete config. -
Add a subscription
Go to “Subscription Group” → “Subscription Group Settings,” add a group, and enter a name and subscription URL. After saving, update it from “Subscription Group.” Choose to update through a proxy or directly, depending on network access.
-
Import a single node
Copy the full link and import it from “Servers” → “Import bulk URL from clipboard.” Put each link on its own line. In v2rayNG, use “+” → “Import from clipboard” on the main screen.
-
Check the fields
Open the node editor and check the server, remote port 443, UUID, transport, path, Host, and SNI. For REALITY nodes, also verify the public key, short ID, fingerprint, and Flow—not just the node name.
-
Test the connection
Select the node, run a test, and check the logs. If they report an unsupported protocol or field, check the core. For address, certificate, or handshake errors, check the node config. Then enable the required system proxy or TUN mode.
6. Conversion options: keep updates or export a snapshot
Converting share links to a Base64 subscription means joining the text and encoding it; converting a Base64 subscription back means decoding and splitting it into lines. This changes only the wrapper: it won’t turn VMess into VLESS or fill in missing server parameters. If the subscription provider changes the nodes, a locally exported file won’t update.
Keep the original subscription URL
RecommendedIf the target client supports the subscription format, add it as a subscription group. You’ll keep receiving the provider’s maintained node list instead of copying a new file for every update. The lists may temporarily differ if the clients update at different times.
Best for: everyday use and sharing a node source between desktop and Android
Export a snapshot of share links
To move a small number of nodes, use the client’s share or export feature, then import them on the target device. Check each transport and security field after export; unfamiliar extension parameters may be lost if you export again.
Best for: moving a single node or troubleshooting how a specific link is parsed
Migrate native JSON
If you need to preserve routing, DNS, or multiple outbounds, check whether the target core supports the complete config instead of converting it to share links first. Also make sure its local ports don’t conflict with listeners already used by the client.
Best for: existing complete configs and control over core behavior
Local encoding example: handle UTF-8 bytes first
These native JavaScript functions encode and decode text locally to illustrate how standard Base64 works. They don’t make network requests, parse nodes, or generate native configs. Use a link list you’ve already confirmed is valid; don’t paste production subscription data into an unfamiliar website’s console.
function encodeList(text) {
const bytes = new TextEncoder().encode(text);
const binary = Array.from(bytes, byte =>
String.fromCharCode(byte)
).join("");
return btoa(binary);
}
function decodeList(encoded) {
let normalized = encoded.trim()
.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);
}
- Join nodes with line breaks before encoding so two URIs don’t run together.
- The decoder handles common URL-safe character substitutions and missing padding; invalid content still throws an error.
- If the decoded result is a single JSON object, determine whether it’s a share payload or a complete config. Don’t import it as a node list without checking.
- To turn local text into an updatable subscription, you also need an accessible service and access controls; encoding alone doesn’t create a subscription URL.
7. Common questions about format conversion and subscription imports
The subscription opens, but why does the client show zero nodes?
First, check the start of the response locally. If it’s HTML, an expiry notice, or an error object, resolve the subscription access issue. If it’s Base64, decode it and check for protocol links on separate lines. If it returns complete JSON, use a compatible custom config option or ask the provider for a format supported by your client.
Why can’t I import JSON even after encoding it?
Base64 doesn’t change the internal structure. A config object containing outbounds is still a config object after encoding; it doesn’t become a list of links. Extract the proxy outbound, map its server, user, transport, and security fields, then export a share link using a compatible tool or client.
The import succeeded, so why does the connection still fail?
A successful import only means the parser accepted the text. Open the node editor and check the remote port, UUID, WebSocket path, and SNI. Then check the logs for certificate, handshake, or unsupported-field errors. Don’t enter the local SOCKS port 10808 as the remote server port.
Where did my routing rules go after switching devices?
Standard node links usually contain only node parameters. Recreate the rules in the target client’s routing settings, or migrate a complete, compatible custom config. Check that outbound tags match their routing references; migrating only the server address and UUID won’t restore all your routing rules.
I shared my subscription URL. Will changing the remark help?
Changing a node remark won’t revoke access. Reset the subscription token or URL in the provider’s management portal, then replace the old URL in the client’s subscription group and update it. If node credentials were exposed too, the server administrator will need to rotate those credentials as well.
Check the three layers in order: verify the response content, confirm the client can parse it, then confirm the core can connect. Recording the client version, selected core, node transport, and error logs makes troubleshooting more effective than repeatedly encoding with Base64. For setup basics, read the Quick Start Guide. For protocol and security fields, see Protocol Reference.