サブスクリプションURLとデータ形式

サブスクリプションURLは設定を取得するための入口で、形式はサーバーが返すデータの構造です。HTTPSで始まるURLでも、Clash YAML、Base64でエンコードされたノードリスト、ログインページなど、返される内容はさまざまです。URLの拡張子だけでは形式を判断できません。.yamlが付いていないURLでも、完全な設定を取得できる場合があります。

問題を切り分ける際は、まず次の3点を確認します。HTTP通信が成功しているか、返されたテキストがどの形式か、使用中のコアがそのプロトコルに対応しているかです。サブスクリプションをYAMLに変換して解決できるのは設定構造の互換性だけです。古いコアに新しいプロトコルへの対応を追加したり、無効になったノードの認証情報を修復したりすることはできません。

よく使われる3形式の構造の違い

形式 見分けるポイント 一般的な内容 形式だけでは判断できないこと
URI形式のノードリンク ss://、trojan://、vmess:// 単一ノードの接続先、認証情報、一部の通信パラメーター ルーティングルール、DNS設定、プロキシグループが含まれるとは限らない
Base64形式のノードリスト デコードするとURIが1行ずつ並ぶ 複数のノードリンクをエンコードしたテキスト デコード結果がClash YAMLとは限らない
Clash YAML proxies:、proxy-groups:、rules:などのキー ノード、プロキシグループ、ルール、および任意のDNS・リスナー設定 すべてのClash系コアが同じフィールドに対応しているとは限らない

URI:単一ノードを共有する形式

URIは一般に「プロトコル、認証情報、サーバー、ポート、クエリパラメーター、備考」で構成されます。ただし、プロトコルごとにフィールドの仕様は異なります。たとえば、よく使われるvmess://形式ではJSONをBase64で包みますが、Shadowsocksの共有形式では認証情報部分だけをエンコードする場合があります。すべてのプロトコルに同じ文字列分割処理を適用しないでください。

以下はフィールドの説明用に作成した架空のTrojanリンクです。example.comはドキュメント用の予約ドメインで、パスワードもサンプル文字列です。利用可能なプロキシサービスではありません。クエリパラメーターのsniはTLSサーバー名、#以降はノードの備考です。

trojan://[email protected]:443?security=tls&type=tcp&sni=example.com#Lab-Trojan

Base64:リスト全体を包むエンコード層

一般的なサブスクリプションでは、複数のURIを改行で区切ったテキスト全体をBase64でエンコードします。正しく処理するには、まず外側のエンコードを解除し、その後、各行を対応するプロトコルのパーサーに渡します。デコード後の行にさらにBase64が含まれていても、それはプロトコル固有の共有形式である可能性があります。テキスト全体をむやみに繰り返しデコードしないでください。

提供元によってはURLセーフな形式を使い、-と_をそれぞれ+と/の代わりに使用したり、末尾の=を省略したりします。これらはエンコード方法の違いであり、別のプロキシプロトコルではありません。

YAML:完全な設定とノードプロバイダーを区別する

完全な設定には、proxies、proxy-groups、rules、dnsなどの設定セクションをまとめられます。proxies:だけを含むファイルは、ノードプロバイダー用として使われることがあります。プロキシグループ、ルール、リスナー設定を備えたクライアント設定一式とは別物です。このファイルを完全な設定としてインポートすると、ノードは表示されてもルールにより通信が直接接続される場合があります。

クライアントが直接インポートできるか

Clash系クライアントは通常YAML設定を使いますが、GUIからのインポート機能はクライアントの実装によって異なります。Clash Verge RevやFlClashなどのインポート機能と、mihomoコアが設定ファイルを読み込む機能は、別のレイヤーです。URIのスキャン、ノードの貼り付け、サブスクリプション変換に対応していても、コアが任意のBase64テキストを設定として読み込めるとは限りません。

「Clash形式」は、将来にわたる固定の互換性を保証するものではありません。従来のClash、Clash Premium、Clash Meta(現在はmihomoと呼ばれることが多い)では、対応機能が異なります。unsupported proxy typeや未知のフィールドに関するエラーが出たら、クライアントのバージョン、実際に動作しているコアのバージョン、プロトコルの種類を確認して、対応状況を調べてください。GUIだけを更新しても、使用中のコアが変わるとは限りません。

ローカル変換:デコードしてからノードのフィールドを割り当てる

手順1:取得した内容がサブスクリプションの本文か確認する

レスポンスをローカルファイルに保存し、テキストエディターで冒頭を確認します。内容が<!DOCTYPE html>、ログイン案内、JSON形式のエラーオブジェクトであれば、まずリクエストの失敗を解決してください。HTTPの200はサーバーがレスポンスを返したことを示すだけで、有効なサブスクリプションとは限りません。401や403は、認証やアクセス制限が原因であることがよくあります。

リクエストパラメーターやUser-Agentに応じて異なる形式を返すサービスもあります。ブラウザーではノードリストがダウンロードされるのに、クライアントではYAMLを取得する場合、サーバー側で形式のネゴシエーションが行われている可能性があります。提供元のドキュメントを確認し、検索で見つけた変換パラメーターをむやみに追加しないでください。

手順2:リストのエンコードをローカルで解除する

以下のNode.jsスクリプトは組み込みモジュールだけを使い、Base64のデコードのみを行います。プロトコル変換機能はありません。エンコードされた本文をsubscription.txtという名前で保存し、スクリプトはdecode-subscription.cjsとして保存してください。入出力ファイルはローカルに置きます。出力ファイルにも機密性の高い認証情報が含まれます。

const fs = require('node:fs');
const { TextDecoder } = require('node:util');

const input = fs.readFileSync('subscription.txt', 'utf8').trim();
const compact = input.replace(/\s+/g, '');

if (!compact || !/^[A-Za-z0-9+/_-]+={0,2}$/.test(compact)) {
  throw new Error('入力は想定されたBase64リストではありません。レスポンス本文を確認してください');
}

const normalized = compact.replace(/-/g, '+').replace(/_/g, '/');
const bare = normalized.replace(/=+$/, '');

if (bare.length % 4 === 1) {
  throw new Error('Base64の長さが不正です。入力が不完全な可能性があります');
}

const bytes = Buffer.from(bare, 'base64');
if (bytes.toString('base64').replace(/=+$/, '') !== bare) {
  throw new Error('Base64の内容が無効か、末尾部分に異常があります');
}

const text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
fs.writeFileSync('nodes.txt', text, { mode: 0o600 });
console.log('nodes.txtに書き込みました。プロトコルと行数をローカルで確認してください');
node decode-subscription.cjs

元の内容がすでにURIリストまたはYAMLの場合、この手順は実行しないでください。ファイル権限の指定が各OSでどのように作用するかは異なります。出力ファイルは個人用ディレクトリに保存し、自動同期フォルダーや公開共有フォルダーは避けてください。デコード後、ノードが1件ずつ別の行にあるか、通信量の説明などノード以外のテキストが混ざっていないか確認します。

手順3:URIのフィールドをYAMLに割り当てる

先ほどのTrojanの例から、以下の最小構成のサンプル設定を作成できます。ローカルの混合プロキシポートは7890、モードはruleに設定し、先に一致しなかった接続はすべて出口選択に渡します。これは構造を説明するための設定です。サンプルのサーバーは接続確認には使えません。

mixed-port: 7890
allow-lan: false
mode: rule
log-level: info

proxies:
  - name: "Lab-Trojan"
    type: trojan
    server: example.com
    port: 443
    password: "training-only"
    sni: example.com
    skip-cert-verify: false

proxy-groups:
  - name: "出口選択"
    type: select
    proxies:
      - "Lab-Trojan"
      - DIRECT

rules:
  - MATCH,出口選択

serverには接続先アドレス、portには整数のポート番号、passwordにはURIの認証情報、sniにはTLS名を指定します。例のtype=tcpをそのままYAMLのtype: tcpに置き換えていないのは、ここでのtypeが通信方式ではなくプロキシプロトコルを示すためです。値はtrojanにする必要があります。このように、フィールド名を機械的に置き換えるだけでは変換できません。

実際のリンクでWebSocket、gRPC、Realityを使っている場合は、対象コアの構文に合わせてパス、Host、サービス名、公開鍵、短いIDなどのパラメーターも割り当てる必要があります。サーバーとパスワードだけを残すと、構文上は正しくても接続できないノードになります。各フィールドの意味が分からない場合は、エラーが出た項目を削除するのではなく、そのプロトコルと通信方式に明確に対応したローカル変換ツールを選んでください。

ルール、プロキシグループ、ノードのフィールドを引き継ぐ方法

元のフィールドまたは設定セクション 変換時の確認事項 引き継がなかった場合に起こりやすい問題
ノード名 名前を一意にし、グループ内の参照名と完全に一致させる プロキシグループの参照エラーや名前の競合
TLSと通信パラメーター SNI、証明書検証、パス、プロトコル拡張 ハンドシェイクの失敗、接続の切断
proxy-groups グループ名、グループ種別、メンバー、テスト設定 接続先を選べない、自動選択の動作が変わる
rules 元の順序、転送先名、末尾のフォールバックルール プロキシ経由にするはずの接続が直接接続になる
dns、tun ローカル設定またはクライアントのオーバーライド設定で管理を続ける 名前解決やトラフィックの処理方法が変わる

URIリストからYAMLを生成する際、ルーティングルールやプロキシグループは通常、変換テンプレートによって新しく作られます。ノード情報から復元されるわけではありません。たとえばテンプレートがDOMAIN-SUFFIX,example.org,DIRECTを生成しても、これはテンプレートのルールであり、元のノードリンクに含まれていた情報ではありません。移行前後でノードのパラメーターとルーティングポリシーをそれぞれ比較し、ノード数だけで判断しないでください。

完全なYAMLからURIを書き出すと、多くの設定情報が失われます。プロキシグループの階層、rule-providers、DNSポリシー、TUN設定には、単一ノードのURIに共通して使える表現がありません。再度変換しても、テンプレートを基に再生成されるだけで、元ファイルのコメントや並び順、すべての機能を復元することはできません。

ノードプロバイダーで変換の繰り返しを減らす

提供元がノード形式のYAMLを配布している場合は、ノードの更新とローカルのルールを分けて管理できます。以下はmihomo設定でローカルのproxy-providersを使う例です。外部の処理で./providers/lab.yamlを更新し、プロキシグループからuseで参照します。このファイルは対応するプロバイダー形式である必要があります。Base64でエンコードされたままのリストは使えません。

proxy-providers:
  lab:
    type: file
    path: ./providers/lab.yaml

proxy-groups:
  - name: "出口選択"
    type: select
    use:
      - lab

rules:
  - MATCH,出口選択

プロバイダーファイルには以下のノード用ラッパー構造を使います。内部の項目にはproxiesと同じフィールドを指定します。相対パスはコアの作業ディレクトリやクライアントの管理方法を基準に解決されるため、手動で開いたYAMLファイルの場所が基準とは限りません。ファイルが見つからない場合は、コアのログで実際に参照されたパスを確認してください。

proxies:
  - name: "Lab-Trojan"
    type: trojan
    server: example.com
    port: 443
    password: "training-only"
    sni: example.com
    skip-cert-verify: false

HTTPプロバイダーを使う場合も、更新先URLは対象コアが受け付ける形式のノードデータを返す必要があります。更新間隔は形式変換機能ではありません。完全な設定に含まれるリモートルールや自動更新URLも確認してください。サブスクリプションに上書きされたくないローカル設定は、クライアントのオーバーライド機能やマージ機能で管理できます。設定画面の場所はクライアントによって異なります。

インポート失敗時と変換後の確認リスト

まずエラーが発生している段階を特定する

順番に再テストする

  1. 元の設定をバックアップし、ノード数、グループ名、ルールモードを記録します。唯一使える設定ファイルを直接上書きしないでください。
  2. 変換後の設定に認証情報と通信パラメーターが含まれているか確認します。機密情報を比較するときはローカルで作業し、画面全体のスクリーンショットを公開の掲示板に投稿しないでください。
  3. mihomoのコマンドライン環境がある場合は、mihomo -t -f ./config.yamlを実行して設定を検証できます。テスト中に外部ファイルを読み込んだり、リモートリソースへアクセスしたりする可能性があるため、事前に設定内容を確認してください。
  4. 読み込み後にノードを手動で選択し、ルールモードが有効か確認します。接続ログで実際に適用されたルールも確認してください。
  5. サンプルのポート7890を使う場合は、curl --proxy http://127.0.0.1:7890 https://example.com/でローカルプロキシの入口を確認できます。このテストでは実際に利用できるノード設定に置き換えてください。TUN、UDP、すべてのルールが検証されたことを示すものではありません。
  6. 最後にサブスクリプションを更新してクライアントを再起動し、ノードが残っているか、オーバーライド設定が有効か、ルールの転送先が存在しない名前に変わっていないか確認します。

変換方法は、提供元が対象のYAMLを直接配布している場合、クライアントが明示的に対応するローカルインポート機能、内容を確認できるローカル変換の順に検討します。長期的に更新するなら、ノードの取得元とローカルルールを分けて管理するほうが、設定全体を何度もURIとYAMLの間で変換するより問題を特定しやすくなります。フィールドの構文は設定リファレンス、クライアントとコアの選び方はクライアント比較を参照してください。