Base64 is an encoding, a subscription is a way to retrieve a collection of nodes, and native JSON is a runtime configuration. They are not the same thing. This guide is for anyone whose subscription opens but won’t import, whose decoded content is still a string, or who is trying to use a share link as a config file. Check each layer—from the subscription response to individual links to core config fields—to determine whether to decode, import, or complete the configuration.
Know the three formats: a subscription URL is not a node config
Bottom line: inspect the content you received before choosing an import option. A subscription URL beginning with https:// is only an endpoint for retrieving data. The response body determines whether the client receives a Base64-encoded node list, plain-text share links, or another format. Base64-decoding the URL itself will not reveal server settings.
Subscription URL vs. node list
RecommendedA common response is a list of share links, one per line, encoded together as Base64. The client saves the subscription URL and fetches the nodes again when you update it. This is useful for maintaining a changing node list, but not every provider uses this response format.
Use when: Your provider gives you a valid subscription URL and you need to keep nodes up to date.
vmess:// share link
This guide covers the common V2 share format: a Base64-encoded node JSON object after the prefix. It describes the information needed for a single VMess outbound, but not a complete local setup with inbound listeners, DNS, and routing policies.
Use for: Moving a single node, importing from the clipboard, or sharing via QR code.
Native core JSON
Usually includes inbounds and outbounds, with optional dns and routing settings. The structure and supported fields depend on the core and its version; a file extension alone cannot tell you whether it is compatible.
Use for: Custom inbound listeners, routing rules, and configuration management for a specific core.
Base64 does not provide confidentiality. Access tokens in subscription URLs, node UUIDs, and share QR codes may all grant connection access. Use a local editor when troubleshooting instead of pasting real data into an online decoder. Remove identifying information from logs before sharing them.
Decode a Base64 subscription: handle the outer list and individual nodes separately
A common two-layer format works like this: decode the outer Base64 to get several vmess:// links; take one link, remove its prefix, then decode again to get the node JSON. Do not assume every input needs exactly two decoding steps. If the subscription body already starts with share links, there is no outer layer to decode.
-
Get the response body
Use a local tool to inspect what the subscription returns. If you see a login page, CAPTCHA, or error page, resolve the access issue first. An HTML page is not a node list, even if the request succeeded.
-
Identify the outer format
If the body contains
vmess://orvless://links, read them line by line. If it is a continuous encoded string, try Base64 decoding. If it starts with{or[, inspect it as JSON first. -
Separate the node lines
Split the decoded result at line breaks, trim whitespace from each line, and discard blank lines. Do not join multiple links together or alter characters within a link.
-
Parse an individual link
For the VMess V2 format covered here, remove
vmess://, then decode and parse the JSON. If the content does not match this structure, check the source’s share format instead of forcing it into these fields.
The function below is for use in your local browser console. It supports standard Base64 and common URL-safe characters, restores omitted padding, and decodes UTF-8 text such as Chinese remarks. Review the code before running it. It makes no network requests and does not access your clipboard.
function decodeBase64Utf8(input) {
let value = input.trim().replace(/\s+/g, "");
value = value.replace(/-/g, "+").replace(/_/g, "/");
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value)) {
throw new Error("Input contains invalid Base64 characters");
}
value = value.replace(/=+$/, "");
if (value.length % 4 === 1) {
throw new Error("Invalid Base64 length");
}
value += "=".repeat((4 - value.length % 4) % 4);
const binary = atob(value);
const bytes = Uint8Array.from(binary, char => char.charCodeAt(0));
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
function readSubscription(body) {
const text = body.trim();
const decoded = /^(vmess|vless):\/\//i.test(text)
? text
: decodeBase64Utf8(text);
return decoded.split(/\r?\n/)
.map(line => line.trim())
.filter(Boolean);
}
function readVmess(link) {
const value = link.trim();
if (!value.startsWith("vmess://")) {
throw new Error("A vmess:// link is required");
}
return JSON.parse(decodeBase64Utf8(value.slice(8)));
}
readSubscription demonstrates just one subscription convention: a plain-text list of links or its Base64-encoded form. It is not a universal subscription parser. If you encounter another structure, stop and check the format documentation from the provider. Successful Base64 decoding only means the data could be read; it does not prove that a node is valid or that your account still has access.
Parsing vmess://: mapping share-link fields to native config
Here is a VMess V2 node object for demonstrating the structure. example.com is a documentation-only example domain, and the UUID is also illustrative; neither can be used to connect to a proxy. The value "2" in field v identifies the share format version, not the client or core version.
{
"v": "2",
"ps": "Documentation example",
"add": "example.com",
"port": "443",
"id": "00000000-0000-4000-8000-000000000001",
"aid": "0",
"scy": "auto",
"net": "ws",
"type": "none",
"host": "example.com",
"path": "/ws",
"tls": "tls",
"sni": "example.com"
}
| Share-link field | Native outbound field | Conversion notes |
|---|---|---|
add、port | settings.vnext[].address、port | The address is a string; the native port is a number. |
id、aid | settings.vnext[].users[].id、alterId | In this example, aid is 0, corresponding to modern VMess AEAD usage. |
scy | settings.vnext[].users[].security | This is the VMess user encryption setting, not a TLS toggle. |
net | streamSettings.network | This example uses ws; changing just one field is not enough to switch transports. |
host、path | wsSettings.headers.Host、wsSettings.path | This is interpreted as WebSocket; preserve any query parameters in the path. |
tls、sni | streamSettings.security、tlsSettings.serverName | The TLS server name and WebSocket Host belong to different layers. |
ps、v | Remarks and share-format metadata | These fields are not required for a core connection and should not be copied as-is into the outbound settings. |
Before converting: preserve transport semantics, then adjust field types
Check the address, port, UUID, transport, TLS, SNI, Host, and path one by one. Do not treat the share field type: "none" as proof that TLS is disabled, and do not overwrite an explicitly provided SNI with the address field.
VMess and VLESS are different protocols. A vless:// link commonly uses a URI with user info, host, port, and query parameters; it cannot be processed by simply removing the prefix and Base64-decoding JSON. Changing a link prefix from VMess to VLESS does not convert the protocol. The server must support the corresponding protocol and identity settings.
Convert to native JSON: add an inbound, not just the node
A native config is more than the decoded node JSON saved under a different filename. Here is a complete example of a common Xray setup using VMess, WebSocket, and TLS: it provides a local SOCKS inbound and uses the example node above for its outbound. Check the fields against your installed core version; this is not a universal config accepted by every V2Fly version.
{
"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": "00000000-0000-4000-8000-000000000001",
"alterId": 0,
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "example.com",
"allowInsecure": false
},
"wsSettings": {
"path": "/ws",
"headers": {
"Host": "example.com"
}
}
}
}
]
}
This example has no routing rules, so all traffic goes to the first outbound by default. It does not set the system proxy automatically; applications must explicitly use the SOCKS service at 127.0.0.1:10808. 127.0.0.1 limits the listener to the local machine. Do not change it to an address exposed on all network interfaces just for troubleshooting.
- Check the port first. If another client is already using 10808, a second core may fail to start. Stop the duplicate instance, or update both the local inbound and the application’s proxy port.
- Keep certificate verification enabled. The example retains
allowInsecure: false. If you see a certificate error, check the system clock, server name, and certificate chain rather than disabling verification. - Know which settings apply. When importing a custom config through a client, whether the system proxy, TUN, and built-in routing remain active depends on the import mode. Do not assume it behaves like a standard node.
Reverse conversion: a share link carries only part of the config
To generate a share link from a single VMess outbound, first extract the server, user, and transport fields from the native config and assemble them into the V2 share object shown above. Encode it as Base64 using UTF-8, then add the vmess:// prefix. Do not Base64-encode the entire native config and add the prefix; the result will not match the format expected by common node importers.
function encodeBase64Utf8(text) {
const bytes = new TextEncoder().encode(text);
let binary = "";
for (const byte of bytes) {
binary += String.fromCharCode(byte);
}
return btoa(binary);
}
function createVmessLink(share) {
return "vmess://" + encodeBase64Utf8(JSON.stringify(share));
}
function createBase64Subscription(links) {
return encodeBase64Utf8(links.join("\n"));
}
createVmessLink takes a share object that has already been assembled according to the field table. It does not extract fields from complex configs or validate node parameters. createBase64Subscription takes an array of share links and generates a common subscription body; it does not create an auto-updating subscription URL. That requires a managed service to provide the response.
- If the native config has multiple servers or users, choose the specific combination to export. Do not assume every item in an array belongs in the same link.
- A single VMess share link usually cannot preserve local inbounds, DNS, routing, log levels, or policies for multiple outbounds.
- Advanced transport fields may be lost on import if the share format has no corresponding convention. For a complete migration, use a config backup and confirm compatibility with the target core.
Choosing a migration method: use links for nodes, backups for policies
Use a share link when you only need to move server connection details. To retain routing, DNS, and local listener settings, use a reviewed config backup. Compare key fields after conversion; a successful import does not prove that the migration preserved everything.
Client imports: use subscriptions, clipboard links, and custom configs for different purposes
A client recognizing an outer format does not mean its core supports every protocol and transport inside it. v2rayN is a desktop client; v2rayNG uses the Xray core; v2flyNG uses the V2Fly core. Match the connection settings to the core’s capabilities before choosing an import option. Menu names and locations below refer to common Chinese-language interfaces and may differ between releases.
| Input format | v2rayN | Android clients |
|---|---|---|
| Subscription URL | Go to “Subscription Groups” → “Subscription Group Settings,” add the URL, then update the group. | In v2rayNG, add the URL under “Subscription Group Settings” in the side menu, then select “Update Subscription” from the main screen menu. In v2flyNG, use the subscription option available in your current version. |
| Single share link | Copy the complete link, then choose “Servers” → “Import bulk URLs from clipboard.” | In v2rayNG, tap “+” in the upper-right corner of the main screen, then select “Import from clipboard.” In v2flyNG, use its share-link import option. |
| Native JSON file | Under the “Servers” menu, choose “Add custom config server” and check which core is in use. | Use the custom config import feature in your current version. Do not paste the config into the subscription URL field. Check the fields before migrating between cores. |
In v2rayN, the common subscription update option is “Subscription Groups” → “Update current subscription (without proxy).” This controls whether a proxy is used to fetch the subscription; it does not change the node’s connection mode to direct. If the subscription site requires a proxy, select the corresponding update-through-proxy option and first confirm that your existing proxy connection works.
- If an update fails, check the response first. If the URL has expired, the access token is invalid, or the response is a web page, do not continue converting node formats. Check the update log and subscription access.
- If an import fails, check the structure first. Look for extra quotation marks, a missing prefix, or a full JSON config mistakenly treated as a share link. If Chinese remarks appear garbled, check the UTF-8 encoding and decoding.
- If a connection fails, check the settings. If the node appears in the list but will not connect, verify the UUID, port, WebSocket path, Host, TLS, and SNI instead of repeatedly re-encoding Base64.
- Save manual edits separately. A subscription update may recreate node entries. Copy temporarily edited nodes to a separate group and note how they differ from the subscription’s original values.
FAQ: Why it still doesn’t work after decoding
If the decoded text still starts with vmess://, did decoding fail?
No. The outer subscription may contain a list of share links, so seeing vmess:// usually means you have successfully retrieved a node line. To inspect the node JSON, decode the content after the prefix of an individual link. If you just want to import it into a client, you can usually use the complete share link as-is.
Can any JSON file be used as a native V2Ray config?
No. A VMess share object, a client settings backup, and a core config may all use JSON, but their fields mean different things. First check that the file has the inbound and outbound structure required by the core, then verify the protocol and transport fields. Valid JSON syntax only means the text can be parsed.
If I encode my local node list, does that create an auto-updating subscription?
You have a subscription body, not a subscription service. Automatic updates also require a stable, access-controlled URL. Saving the body locally will not automatically send changes to other devices. If you only need to share nodes once, share links are more straightforward.
If a client imports the config successfully, does that mean all connection settings are present?
Not necessarily. A parser may accept basic fields but ignore unsupported options. After importing, open the node editor and check the transport, path, Host, and TLS server name before testing the connection. A QR code is only a way to carry text; it cannot fill in missing settings.