V2Ray 訂閱連結格式解析:Base64 清單、原生 JSON 與 vmess:// 分享連結互轉

解析常見訂閱與分享格式,了解 v2rayN、v2rayNG 支援的格式,以及解碼與轉換時容易出錯的欄位。

本文重點

訂閱網址是取得設定的入口;Base64 是文字編碼;vmess:// 是節點分享格式;原生 JSON 則描述核心的運作方式。本文適合需要在不同裝置間匯入節點、檢查訂閱內容或移轉自訂設定的使用者:先辨識回應格式,再於本機解碼,最後核對協定、傳輸與 TLS 欄位,避免把「解析成功」誤當成「設定完整」。

先分清訂閱網址、節點清單與原生 JSON

「V2Ray 訂閱」並非統一的檔案格式。HTTPS 網址可能回傳 Base64 編碼的分享連結清單、純文字清單、完整 JSON,甚至只是登入頁面。副檔名或網址中是否含有「subscribe」,都無法決定內容;用戶端實際處理的是伺服器回傳的回應本文。

常見的 Base64 訂閱有兩層結構:外層將整份清單編碼一次;解碼後,每一行都是一條分享連結。如果其中一行是傳統 JSON 格式的 vmess://,協定前綴後的負載又經過一次 Base64 編碼。兩層編碼的對象不同,不能直接把整份訂閱當成單一 VMess 節點解析。

分享連結清單訂閱

推薦

提供多條分享連結,方便管理一般節點。服務商更新網址或連接埠後,用戶端可透過更新訂閱重新取得清單;外層是否使用 Base64,則視服務商的輸出方式而定。

適用情境:在 v2rayN 與 v2rayNG 中管理來自同一來源的節點

單一 vmess:// 分享連結

描述單一 VMess 節點的連線參數,不等於可持續更新的訂閱網址。通常透過剪貼簿或 QR 碼匯入。

適用情境:暫時移轉單一節點,或逐一排查匯入問題

核心原生 JSON

可包含入站、多個出站、DNS、路由等內容。它不只是節點清單,且必須符合實際使用的核心設定格式。

適用情境:需要明確控制入站與路由的自訂設定

v2rayN 與 v2rayNG 應從哪裡匯入

先依內容選擇匯入入口,再考慮轉換格式。將 HTTPS 訂閱網址從剪貼簿匯入為節點,或把完整 JSON 貼進訂閱網址欄位,通常都無法得到預期結果。以下採用常見的中文介面名稱;不同版本的選單用詞可能略有差異,請依入口功能確認。

在兩款用戶端使用同一份分享連結訂閱

桌面版:v2rayN
  • 開啟「訂閱分組」→「訂閱分組設定」,新增分組並填入訂閱網址。
  • 儲存後,在「訂閱分組」選單中執行「更新全部訂閱(不透過代理)」;若該來源必須透過代理存取,則選擇透過代理更新。
  • 只有單一分享連結時,請使用「伺服器」→「從剪貼簿匯入分享連結」,不要新增網址為 vmess:// 的訂閱分組。
Android 版:v2rayNG
  • 從側邊欄開啟「訂閱分組設定」,點選新增按鈕,填入備註與訂閱 URL 後儲存。
  • 返回主畫面,從右上角選單執行「更新訂閱」,並確認清單中是否出現該分組的節點。
  • 單一分享連結請使用主畫面的「+」→「從剪貼簿匯入」;完整設定則使用自訂設定的檔案或剪貼簿匯入入口。

同一來源不代表兩款用戶端支援完全相同的參數;更新後仍須確認節點數量、傳輸類型與 TLS 設定。

兩款用戶端都能處理常見的傳統 JSON 格式 VMess 分享連結,以及常見的分享連結清單訂閱。完整原生 JSON 應透過自訂設定入口匯入,不能因為「支援 VMess」就推斷其支援任意 JSON 訂閱結構。有些設定包含核心專用欄位,必須一併確認用戶端版本與所選核心。

v2rayNG 使用 Xray 核心;v2flyNG 使用 v2fly 核心。兩者並非只換個介面的同一套設定解析器。將 Xray 專用設定交給 v2flyNG 前,應檢查協定與欄位的支援情況,而非只變更檔名。v2rayN 則須確認該設定實際交由哪個核心執行。

本機解碼:先檢查清單,再檢查 vmess:// 負載

以下 JavaScript 僅用於在本機瀏覽器的開發者工具中檢視文字,不會發出網路請求。請先閱讀程式碼,再於離線空白頁面的主控台執行。函式接收已複製到本機的本文,不會下載訂閱;請勿在陌生網頁的主控台中處理憑證。

function decodeBase64Utf8(input) {
  let text = input.trim().replace(/\s+/g, "");
  text = text.replace(/-/g, "+").replace(/_/g, "/");
  if (text.length % 4 === 1) {
    throw new Error("Base64 長度不合法");
  }
  text = text.padEnd(Math.ceil(text.length / 4) * 4, "=");
  const bytes = Uint8Array.from(atob(text), ch => ch.charCodeAt(0));
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}

function encodeBase64Utf8(text) {
  const bytes = new TextEncoder().encode(text);
  let binary = "";
  for (const byte of bytes) binary += String.fromCharCode(byte);
  return btoa(binary);
}

function decodeVmessJson(uri) {
  const text = uri.trim();
  if (!text.startsWith("vmess://")) {
    throw new Error("不是 VMess 分享連結");
  }
  return JSON.parse(decodeBase64Utf8(text.slice(8)));
}

// 自我檢查僅使用本機範例,不含訂閱憑證。
console.log(decodeBase64Utf8(encodeBase64Utf8("節點範例")));
  1. 辨識回應:若本文已逐行列出 vmess:// 等協定前綴,請直接逐行處理,不要再將全文解碼。若開頭是 HTML 標籤或登入提示,應先排除訂閱請求問題。
  2. 解開外層:確認清單本文為 Base64 後,呼叫 decodeBase64Utf8,再使用 split(/\r?\n/) 逐行拆分並篩除空行。
  3. 解開單一連結:將其中一條傳統 JSON 格式的 VMess 連結交給 decodeVmessJson。預期結果是包含 add、port、id 等欄位的物件。
  4. 停止盲目重試:若解碼結果不是 JSON,請確認分享格式的變體。並非所有使用相同協定前綴的舊版實作,都採用本文所述的 JSON 負載格式。

atob 回傳的是代表位元組的字串,並未直接完成 UTF-8 解碼。省略 TextDecoder 時,中文節點名稱可能會變成亂碼;直接對中文字串呼叫 btoa 則可能發生錯誤。也要區分 Base64URL 的 -、_ 與標準 Base64 的 +、/;不能直接將訂閱 URL 進行上述字元替換。

4 個字元
每組 Base64 最多表示 3 個位元組
443
下方範例中的伺服器 TLS 連接埠
10808
下方範例中的本機 SOCKS 入站連接埠

vmess:// 與原生 JSON 欄位如何對應

以下是用於教學的傳統 VMess 分享負載;網域與 UUID 僅供展示結構,並不代表有可連線的服務。v 是分享格式版本,不是核心版本;aid 是 alterId。現代 VMess AEAD 設定通常為 0,但移轉時仍應以伺服器要求為準。

{
  "v": "2",
  "ps": "VMess WS TLS 範例",
  "add": "node.example.com",
  "port": "443",
  "id": "8a7e4f30-5b91-4f8e-9e42-2c6d1b0a73f5",
  "aid": "0",
  "scy": "auto",
  "net": "ws",
  "type": "none",
  "host": "node.example.com",
  "path": "/gateway",
  "tls": "tls",
  "sni": "node.example.com"
}
分享欄位常見核心設定位置轉換注意事項
add / portsettings.vnext[].address / port分享連結中的連接埠通常是字串,核心設定則使用數字。
id / aid / scyusers[].id / alterId / security分別代表 UUID、alterId 與 VMess 內容加密方式。
netstreamSettings.network本例使用 ws;不能只改名稱就轉換為其他傳輸方式。
host / pathwsSettings.headers.Host / path這是 WebSocket 請求參數,不一定等於伺服器位址。
tls / snistreamSettings.security / tlsSettings.serverNameTLS 與 VMess 內容加密是兩種不同層次的設定。
ps用戶端備註或自訂出站標籤備註不是連線參數,也不會自動產生路由規則。

以下完整範例採用常見的 vnext 結構,只展示一個本機 SOCKS 入站與一個 VMess 出站。匯入用戶端後是否保留入站設定,取決於自訂設定的執行方式。執行前也應確認目前使用的核心支援這組欄位結構。

{
  "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": "node.example.com",
            "port": 443,
            "users": [
              {
                "id": "8a7e4f30-5b91-4f8e-9e42-2c6d1b0a73f5",
                "alterId": 0,
                "security": "auto"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "serverName": "node.example.com",
          "allowInsecure": false
        },
        "wsSettings": {
          "path": "/gateway",
          "headers": {
            "Host": "node.example.com"
          }
        }
      }
    }
  ]
}

重新編碼與反向轉換:哪些資訊會遺失

將傳統 JSON 格式的 VMess 負載轉成分享連結,只需將 JSON 以 UTF-8 編碼,再加上協定前綴。將多條連結以換行串接後整體編碼,即可取得常見的 Base64 清單本文。以下程式碼接續前面的編碼函式,輸入仍是教學範例。

const sample = {
  v: "2",
  ps: "VMess WS TLS 範例",
  add: "node.example.com",
  port: "443",
  id: "8a7e4f30-5b91-4f8e-9e42-2c6d1b0a73f5",
  aid: "0",
  scy: "auto",
  net: "ws",
  type: "none",
  host: "node.example.com",
  path: "/gateway",
  tls: "tls",
  sni: "node.example.com"
};

const share = "vmess://" + encodeBase64Utf8(JSON.stringify(sample));
const links = [share];
const subscriptionBody = encodeBase64Utf8(links.join("\n"));

console.log(share);
console.log(subscriptionBody);

產生清單本文不等於建立訂閱服務。只有將內容放在用戶端可存取的網址,並設定適當的存取控制,才算建立可更新的遠端入口。若只是要在裝置間移轉,直接使用用戶端匯出的分享連結即可,不必額外架設檔案。

排查匯入失敗、節點數為零與中文亂碼

訂閱更新完成,為什麼節點數量還是零?

先確認目前檢視的是正確分組,再查看更新記錄中的解析提示。在本機檢視回應本文:若內容是登入頁面、權杖過期提示或不支援的設定結構,重複進行 Base64 解碼並無幫助。請向來源方取得適用於 v2rayN、v2rayNG 的分享連結清單。

解碼時發生錯誤,是不是少複製了一段?

檢查文字是否被聊天工具插入省略號、是否混入引號,以及長度除以 4 後的餘數是否為 1。缺少結尾填充符號時可以正規化處理,但本文若已遭截斷,無法靠補上等號還原。不要對完整的 HTTPS 訂閱網址使用解碼函式。

節點名稱變成亂碼,連線參數還能用嗎?

先以 UTF-8 重新解碼,並檢查負載中的 ps。若只有備註出現亂碼,通常不會直接影響連線;若路徑或其他欄位也含有非 ASCII 字元,則必須逐項核對。請保留原始文字,不要對亂碼結果反覆轉碼。

匯入成功但連線逾時,還需要重新轉換格式嗎?

先確認伺服器位址及 443 連接埠是否確實為服務商提供的值,再檢查 UUID、WS 路徑、Host 與 SNI。若記錄指出 10808 連接埠已被占用,應處理本機入站衝突;若是 TLS 名稱錯誤,則應修正伺服器名稱,而非反覆編碼相同內容。

手動修改的節點,為什麼訂閱更新後又恢復原樣?

訂閱節點通常會根據遠端內容重新產生。若要保留本機修改,請先匯出或複製為獨立設定,再調整欄位;實際保留方式依用戶端版本而異。訂閱更新的是來源資料,不會自動將本機編輯同步回服務商。

可靠的移轉應保留三份可供比對的資訊:原始訂閱本文、解碼後的節點欄位,以及匯入後的用戶端設定。驗證時先用單一節點確認連線,再擴大到整份清單。若要移轉完整 JSON 中的 DNS 與路由設定,可搭配進階設定逐項檢查;不熟悉欄位含義時,先查閱術語表,比接連嘗試多種轉換工具更容易找出差異。

下載用戶端v2rayN / v2rayNG