First, distinguish the subscription URL, its response, and node share links. Then compare a VLESS + WebSocket + TLS example in URI and Xray JSON form. This guide is for anyone whose subscription opens but won’t import, whose decoded Base64 still isn’t a config, or who needs to migrate nodes. You’ll learn which import option to use and which routing, DNS, and local listener settings may be lost during conversion.
How subscription URLs, Base64, and JSON differ
A subscription is usually an HTTPS URL that a client fetches to retrieve node data. The URL itself is neither Base64 nor a node config; the key is the response body returned by the server. The same URL may return different content depending on request parameters or client identifiers, so its file extension alone won’t tell you the format.
| Content type | How to identify it | Where to use it and limitations |
|---|---|---|
| Subscription URL | Starts with https://; may include an access token | Add it in subscription settings to keep future updates enabled |
| Base64 subscription body | Often decodes to share links, one per line | Usually parsed by the client’s subscription updater; not a complete runtime config |
| Single-node share link | Starts with vless:// or, commonly, vmess:// | Suitable for importing a node from the clipboard; usually doesn’t include local routing |
| Native core JSON | Contains fields such as inbounds and outbounds | Use the custom config option for the matching core; supported fields depend on that core |
Base64 is an encoding for bytes, not encryption. Anyone with the subscription body can usually recover server addresses and identity details. JSON is just a data format: text beginning with braces might describe a node, contain an API error, or be a complete core config. Check the fields inside; valid JSON is not necessarily ready to run.
Decode and re-encode a Base64 subscription
A common Base64 subscription combines multiple share links with newline characters, then encodes the resulting UTF-8 text. If decoding reveals multiple lines beginning with vless:// or vmess://, you’ve recovered the outer subscription; there’s no need to Base64-decode each VLESS link. Some services return a plain-text list of links, in which case skip the outer decoding step.
- First confirm you have the response body—not a browser login page, access restriction notice, or error message with a status code.
- For a suspected Base64 body, remove spaces and line breaks, support the URL-safe characters
-and_, then restore any missing padding. - Read the decoded bytes as UTF-8, check each line’s protocol prefix, and preserve the parameters within each link.
- Before re-encoding, confirm that the client actually needs a Base64 list. Encoding text locally creates a static string; it does not create an HTTPS subscription service that updates automatically.
The plain JavaScript function below can be run in your local browser’s developer tools. It uses byte arrays to handle Chinese remarks, avoiding errors from using btoa directly on non-ASCII text. It only converts the input text and makes no network requests; use content you trust.
function decodeSubscription(source) {
const compact = source.trim().replace(/\s+/g, "")
.replace(/-/g, "+").replace(/_/g, "/");
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(compact)) {
throw new Error("Input is not a valid Base64 character sequence");
}
const padded = compact + "=".repeat((4 - compact.length % 4) % 4);
const bytes = Uint8Array.from(atob(padded), ch => ch.charCodeAt(0));
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
function encodeSubscription(linkText) {
const bytes = new TextEncoder().encode(linkText);
let binary = "";
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary);
}
const sampleText = "Sample node";
console.log(decodeSubscription(encodeSubscription(sampleText)));
If the decoder throws an error, don’t keep applying layers of conversion. Check whether the response is truncated or includes quotes or HTML tags, then confirm whether it’s already a plain-text list. Even after successful decoding, the client still needs to validate each link’s protocol, required fields, and transport settings.
Field structure of vless:// and vmess:// links
VLESS share links typically use a URI structure: the user-info section contains the UUID, the host and port identify the server, query parameters describe transport and security, and the text after the hash is the node remark. The example below uses a reserved example domain and a demonstration UUID for illustrating the fields only; it doesn’t represent a connectable service.
vless://[email protected]:443?encryption=none&security=tls&type=ws&host=example.net&path=%2Fws&sni=example.net#WS-TLS-demo
path=%2Fws decodes to /ws. Encode paths, remarks, and special characters in query parameters using percent encoding. Don’t encode the entire URI at once or decode the full URI before parsing it manually, or characters such as & in a path could be mistaken for parameter separators. IPv6 addresses in the host position must also be enclosed in square brackets.
VLESS share link
- Identity
- UUID appears before @
- Transport
- type=ws
- Security
- security=tls
- Path
- path=%2Fws
This example uses WebSocket and TLS; don’t add Vision flow.
Common VMess share format
- Outer layer
- Base64 follows vmess://
- Server
- add and port in the JSON
- Identity
- id in the JSON
- Transport
- net and path in the JSON
This is a common client-side sharing convention, not JSON that can be passed directly to a core for runtime use.
In common VMess share JSON, v is the share format version, not the VMess protocol version; ps is usually the remark. Its structure differs from settings.vnext in a core outbound. VMess sharing conventions also have variants, so when migrating, use the import and export features in both clients where possible rather than assuming every vmess:// link has exactly the same internal structure.
Build native Xray JSON from a VLESS link
To turn a share link into a complete JSON config, you need to add local inbounds and outbound tags that the link doesn’t contain. The config below matches the earlier VLESS example and uses the Xray config structure; it listens locally only on 127.0.0.1:10808. Before using it, replace the example values with real server parameters you’re authorized to use. The example domain does not provide proxy service.
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "example.net",
"port": 443,
"users": [
{
"id": "9f2a54d1-8bc6-4d71-a903-0be458e623fd",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "example.net"
},
"wsSettings": {
"path": "/ws",
"headers": {
"Host": "example.net"
}
}
}
}
]
}
example.net:443maps to the outboundaddressandport; the UUID maps tousers[0].id.type=wsmaps tostreamSettings.network;security=tlssets the transport security layer.snimaps totlsSettings.serverName;hostmaps to the WebSocket Host request header. They serve different purposes.encryption=noneis a VLESS user field; it does not mean TLS is disabled. CheckstreamSettings.securityto see whether TLS is enabled.- This example has no additional routing rules, so traffic uses the first outbound. It also doesn’t enable proxy settings in your operating system or applications.
Choose the right import option in v2rayN or v2rayNG
First decide whether you need to preserve the subscription or import a single node. Decoding a subscription and importing its links one by one may create nodes, but usually loses the subscription source and automatic updates. To keep receiving server list updates, save the original subscription URL instead of just the currently exported share links.
v2rayN: handle subscriptions, clipboard links, and custom configs separately
- Subscription URL: Go to “Subscription Group” → “Subscription Group Settings,” add a group, and enter a remark and URL. Save it, then use the update option under “Subscription Group.” After updating, check that the nodes were added; don’t enter the URL as if it were a single-node address.
- Share links: Copy the complete
vless://orvmess://text, then choose “Server” → “Import bulk URLs from clipboard.” After importing, edit the node and check the address, port, TLS, SNI, and transport path. - Complete JSON: Use a custom config option such as “Server” → “Add custom config server.” Choose the matching core, then select the config file as prompted. Don’t paste an entire JSON config into the subscription URL field.
v2rayNG: create a subscription group, then update it
- From the main screen menu, open “Subscription group settings.” Tap the add button, enter a subscription name and address, and save. Return to the main screen and select “Update subscription.”
- For a single share link, copy the complete text, then tap “+” on the main screen → “Import from clipboard.” Make sure the generated config type matches the source before connecting.
- For native Xray JSON, use the custom config import option in the “+” menu, choosing a file or clipboard import method as supported by your version. Don’t import common VMess share JSON as a complete custom config.
Menu labels may vary slightly between releases. Look for options corresponding to “subscription group,” “clipboard link,” and “custom config.” With v2flyNG, also check that the subscription’s protocols and transports are compatible. The fact that v2rayNG recognizes an Xray node doesn’t mean another core can run the same config.
Exporting JSON back to links and what conversion loses
“Convert between formats” doesn’t mean all three formats can always make a complete round trip. A single-node link mainly describes the remote connection, while a full JSON config may include multiple inbounds and outbounds, DNS, routing, policies, and logs. Exporting JSON as a single VLESS link means choosing one outbound and extracting its connection parameters; the remaining local behavior usually can’t be represented in the link.
| Conversion | Usually preserved | Requires extra work |
|---|---|---|
| Link list ↔ Base64 | Original link text and order | Character encoding, line breaks, and URL-safe variants |
| VLESS link → JSON | Address, identity, common transport and security parameters | Add inbounds, outbound tags, and local routing |
| JSON outbound → share link | Single-node fields supported by the target client | Choose an outbound, handle extension fields, and save DNS and routing rules separately |
| VMess → VLESS | Shared address and transport details can only serve as a reference | The server must actually provide VLESS; changing the prefix won’t switch protocols |
When migrating, select a node in the source client and use its share-link export feature, then check the fields in the target client. If you only have JSON with multiple outbounds, identify the target by checking tag and protocol in outbounds; don’t mistake a direct or block outbound for a server. For extension parameters, keep a backup of the original JSON and inspect the config generated by the target client.
- Migrating one node: use the share link exported by the client, then check the transport and security parameters.
- Migrating a subscription: save the original subscription URL and create a subscription group again in the target client.
- Migrating the full setup: back up routing, DNS, and custom configs separately, and check for local port conflicts.
Subscription decoding and import FAQs
Why does the subscription open in a browser but fail to update in the client?
First check whether the response is actually a node list, rather than a login page or API error JSON. Then make sure the complete URL includes its token and query parameters. If the address isn’t accessible from your current network, you can update through a proxy if you already have a working node. Don’t rely on a node you haven’t imported yet as the only network route for updating the subscription.
Why does Base64 decode successfully but show no servers to import?
Check the protocol prefix on every line of the decoded result. Successful Base64 decoding only proves that the encoding was reversible; it doesn’t mean the content is supported by the client. If the result is JSON, determine whether it’s a share-field object or a complete config. If it’s HTML, troubleshoot the subscription request instead of encoding it again.
What if a share link imports but the connection still times out?
Open the node editor and compare the port, transport path, TLS, and SNI with the source. In this example, the remote port is 443; 10808 is only the local SOCKS listening port, and the two aren’t interchangeable. Then check the core logs to distinguish DNS resolution issues, connection timeouts, and TLS handshake errors. “Import successful” only means the parser accepted the fields.
Why did my mainland China direct-routing rules disappear after conversion?
Node share links usually don’t include the complete local routing setup. Restore routing separately in the target client. If you use a custom JSON config, check that routing.rules and the outbound tags match. If you need domainStrategy, place it at the routing level, not inside an individual rule.
For final verification, change one layer at a time: first confirm the content format, then check that all fields are present, and finally test the network and core. This separates subscription retrieval failures, link parsing failures, and node connection failures, avoiding repeated guesswork across encoding tools and proxy settings.