mcpField
C
custom-domains

custom-domains

github.com/dfinityクラウド・DevOps39199 回インストール今日更新
リポジトリ

custom-domains について

Internet Computer の canister をカスタムドメインで公開するための手順をまとめたスキルです。DNS 設定、所有権確認、登録、TLS 証明書の発行、ドメイン更新を扱います。

主な機能

  1. 01DNS レコードの設定
  2. 02ドメイン所有権ファイルの配置
  3. 03ドメインの検証と登録
  4. 04証明書と登録状態の確認
  5. 05ドメインの更新と削除

活用シーン

  1. 01ブランドドメインで canister を公開
  2. 02カスタムドメイン登録の失敗を調査
  3. 03別の canister へのドメイン移行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dfinity/icskills --skill custom-domains

インストール後、Claude Code で/custom-domainsを入力して読み込まれたか確認します。

詳細

カスタムドメイン

概要

デフォルトでは、キャニスターには <canister-id>.icp.net でアクセスできます。カスタムドメインサービスを使うと、独自のドメイン(例:yourdomain.com)で任意のキャニスターを提供できます。DNS を設定し、ドメイン所有権ファイルをキャニスターにデプロイして、REST API 経由で登録します。HTTP ゲートウェイは、TLS 証明書の発行と更新、ルーティングを自動的に処理します。

カスタムドメインは境界ノードレベルで動作し、DNS を介してドメインを任意のキャニスター ID にマッピングします。これは、アセットキャニスターだけでなく、HTTP 経由で /.well-known/ic-domains を提供できるあらゆるキャニスターで機能します。これには、アセットキャニスター、Juno サテライト、http_request を実装するカスタムキャニスターが含まれます。

前提条件

  • いずれかのレジストラから登録したドメイン(例:Namecheap、GoDaddy、Cloudflare)
  • そのドメインの DNS レコードを編集する権限
  • HTTP 経由で /.well-known/ic-domains を提供するデプロイ済みキャニスター(アセットキャニスター、Juno サテライト、または http_request を実装する任意のキャニスター)
  • 登録 API 呼び出し用の curl
  • jq(任意。JSON レスポンスの整形用)

セットアップを壊すよくあるミス

  1. DNS プロバイダーの SSL/TLS を無効にしていない。 Cloudflare などのプロバイダーでは、デフォルトで Universal SSL が有効になっています。これは、IC が証明書を発行するために使用する ACME チャレンジを妨げ、証明書の更新を阻止することがあります。登録前に、DNS プロバイダーが提供する証明書/SSL/TLS の機能を無効にしてください。

  2. apex ドメインに CNAME を設定している。 多くの DNS プロバイダーでは、apex(サブドメインのない example.com など)に CNAME レコードを設定できません。プロバイダーが対応している場合は、ANAME または ALIAS レコードタイプ(CNAME フラットニング)を使用してください。対応していない場合は、www.example.com などのサブドメインを使用してください。

  3. _acme-challenge CNAME がない。 _acme-challenge.CUSTOM_DOMAIN が _acme-challenge.CUSTOM_DOMAIN.icp2.io を指していないと、HTTP ゲートウェイは TLS 証明書を取得できません。登録は失敗します。

  4. _canister-id に複数の TXT レコードがある。 _canister-id.CUSTOM_DOMAIN に複数の TXT レコードが存在すると、登録は失敗します。キャニスター ID を含むレコードを 1 つだけ残してください。

  5. .well-known/ic-domains ファイルを忘れている。 キャニスターは、カスタムドメインを記載した /.well-known/ic-domains を提供する必要があります。このファイルがないと、登録時のドメイン所有権の確認に失敗します。

  6. DNS プロバイダーの古い _acme-challenge TXT レコードが残っている。 プロバイダーが以前に行った ACME チャレンジによって、ダッシュボードには表示されない TXT レコードが _acme-challenge.CUSTOM_DOMAIN に残っていることがあります。これらは IC の ACME フローと競合します。プロバイダーが提供するすべての TLS 機能を無効にして、レコードを削除してください。dig TXT _acme-challenge.CUSTOM_DOMAIN で確認できます。

  7. ドメインを明示的に登録していない。 DNS の設定だけでは不十分です。登録を開始するには、POST /custom-domains/v1/CUSTOM_DOMAIN を呼び出す必要があります。自動的には登録されません。

  8. HttpAgent の host にカスタムドメインを設定している。 host はキャニスター呼び出しの送信先となる API エンドポイント であり、フロントエンドの提供元ドメインではありません。カスタムドメインは HTTP ゲートウェイであり、/api/v2 を提供しません。そのため、host にカスタムドメイン(または window.location.origin)を指定すると、呼び出しは失敗します。host を設定する必要はありません。最近の @icp-sdk/core の HttpAgent は、カスタムドメイン上で host が省略されている場合、https://icp-api.io(メインネット API 境界ノード)に解決します。未設定のままにするか、明示的に https://icp-api.io を指定してください。ゲートウェイドメインは指定しないでください。

  9. すでにユーザーがいるアプリにカスタムドメインを追加している。 II プリンシパルはオリジンごとに異なるため、新しいドメインでは、再訪するすべてのユーザーにまったく新しいアカウントが割り当てられます。派生元を固定してください。カスタムドメインに置き換えられることのないキャニスターアドレスを使い、ドメインが有効になる 前に .well-known/ii-alternative-origins にそのドメインを記載してください。すでにサインインを収集したドメインは、アカウントを孤立させずに転用することはできません。internet-identity スキルを参照してください。

実装

ステップ 1:DNS レコードを設定する

3 つの DNS レコードを追加します(CUSTOM_DOMAIN をドメインに置き換えてください。例:app.example.com)。

レコードタイプホスト値
CNAMECUSTOM_DOMAINCUSTOM_DOMAIN.icp1.io
TXT_canister-id.CUSTOM_DOMAINお使いの Canister ID(例:hwvjt-wqaaa-aaaam-qadra-cai)
CNAME_acme-challenge.CUSTOM_DOMAIN_acme-challenge.CUSTOM_DOMAIN.icp2.io

一部の DNS プロバイダーでは、メインドメインのサフィックスが省略されます。このようなプロバイダーで app.example.com を使用する場合:

  • app.example.com の代わりに app
  • _canister-id.app.example.com の代わりに _canister-id.app
  • _acme-challenge.app.example.com の代わりに _acme-challenge.app

CNAME に対応していないネイキッドドメインでは、プロバイダーの ANAME または ALIAS レコードタイプを使用して、CUSTOM_DOMAIN.icp1.io を指定してください。

ステップ 2:ic-domains ファイルを作成する

Canister は HTTP 経由で /.well-known/ic-domains を配信する必要があります。このファイルを作成し、カスタムドメインを 1 行に 1 つずつ記載してください。

app.example.com
www.example.com

フロントエンド Canister のユーザー向け: .well-known/ を public/ ディレクトリ(Vite プロジェクトの場合)内、またはソースファイルと同じ場所に配置し、dir に含まれるようにしてください。推奨の @dfinity/static-site レシピでは、.well-known/ は自動的にアップロードされます(設定は不要です)。従来の @dfinity/asset-canister レシピでは、非表示ディレクトリがデプロイされるように .ic-assets.json5 に { "match": ".well-known", "ignore": false } が含まれていることを確認してください。ファイルの配置について詳しくは、static-site スキルを参照してください。

カスタム http_request Canister 向け: HTTP リクエストハンドラーから /.well-known/ic-domains にファイルの内容を直接配信してください。

ステップ 3:デプロイする

Canister をデプロイし、https://<canister-id>.icp.net/.well-known/ic-domains で /.well-known/ic-domains にアクセスできるようにします。

ステップ 4:検証する

登録する前に、DNS レコードと Canister の設定を確認してください。

curl -sL -X GET "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN/validate" | jq

成功時の応答:

{
  "status": "success",
  "message": "Domain is eligible for registration: DNS records are valid and canister ownership is verified",
  "data": {
    "domain": "CUSTOM_DOMAIN",
    "canister_id": "CANISTER_ID",
    "validation_status": "valid"
  }
}

検証に失敗した場合によくあるエラーと対処方法:

エラー対処方法
DNS CNAME レコードがありません_acme-challenge.CUSTOM_DOMAIN.icp2.io を指定する _acme-challenge CNAME を追加します
DNS TXT レコードがありませんCanister ID を値とする _canister-id TXT レコードを追加します
DNS TXT レコードが無効ですTXT の値が有効な Canister ID であることを確認します
DNS TXT レコードが複数あります重複する _canister-id TXT レコードを削除し、1 つだけ残します
登録済みドメインを取得できませんでした.well-known/ic-domains が Canister にデプロイされ、配信されていることを確認します
ドメインがリストにありませんic-domains ファイルにドメインを追加し、再デプロイします
ステップ 5:登録する
curl -sL -X POST "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN" | jq

成功時の応答:

{
  "status": "success",
  "message": "Domain registration request accepted and may take a few minutes to process",
  "data": {
    "domain": "CUSTOM_DOMAIN",
    "canister_id": "CANISTER_ID"
  }
}
ステップ 6:証明書のプロビジョニングを待つ

registration_status が registered になるまでポーリングします。

curl -sL -X GET "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN" | jq

ステータスの値:registering → registered(成功)、または failed(エラーメッセージを確認してください)。

registered になった後、すべての HTTP ゲートウェイに反映されるまでさらに数分待ってからテストしてください。

カスタムドメインを更新する

既存のカスタムドメインの接続先を別の Canister に変更するには:

  1. _canister-id TXT レコードを新しい Canister ID に更新します。
  2. サービスに通知します。
curl -sL -X PATCH "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN" | jq
  1. ステータスを確認します。
curl -sL -X GET "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN" | jq

カスタムドメインを削除する

  1. DNS から _canister-id TXT レコードと _acme-challenge CNAME を削除します。
  2. サービスに通知します。
curl -sL -X DELETE "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN" | jq
  1. 削除を確認します(404 が返るはずです)。
curl -sL -X GET "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN" | jq

HttpAgent の設定

カスタムドメインで配信されるフロントエンドも、Canister への呼び出しには配信元のドメインではなく、メインネットの API 境界ノード(https://icp-api.io)を使用します。HttpAgent の host はこの API エンドポイントであり、フロントエンドのオリジンではありません。カスタムドメインは HTTP ゲートウェイであり、/api/v2 は配信しません。

host を設定する必要はありません。省略すると、最近の @icp-sdk/core の HttpAgent は、カスタムドメイン(および icp.net)で https://icp-api.io を解決先として使用します。host にカスタムドメインや window.location.origin を指定しないでください。これらはゲートウェイであるため、呼び出しに失敗します。

import { HttpAgent } from "@icp-sdk/core/agent";

// host omitted on a custom domain → resolves to https://icp-api.io
const agent = await HttpAgent.create();

// equivalent, explicit:
const agentExplicit = await HttpAgent.create({ host: "https://icp-api.io" });

デプロイとテスト

# 1. Deploy your canister with the ic-domains file served at /.well-known/ic-domains

# 2. Validate DNS + canister config
curl -sL -X GET "https://icp.net/custom-domains/v1/yourdomain.com/validate" | jq

# 3. Register
curl -sL -X POST "https://icp.net/custom-domains/v1/yourdomain.com" | jq

# 4. Poll until registered
curl -sL -X GET "https://icp.net/custom-domains/v1/yourdomain.com" | jq

動作を確認する

# 1. Verify DNS records
dig CNAME yourdomain.com
# Expected: yourdomain.com. CNAME yourdomain.com.icp1.io.

dig TXT _canister-id.yourdomain.com
# Expected: "<your-canister-id>"

dig CNAME _acme-challenge.yourdomain.com
# Expected: _acme-challenge.yourdomain.com. CNAME _acme-challenge.yourdomain.com.icp2.io.

# 2. Verify ic-domains file is served by the canister
curl -sL "https://<canister-id>.icp.net/.well-known/ic-domains"
# Expected: your domain listed

# 3. Verify registration status is "registered"
curl -sL -X GET "https://icp.net/custom-domains/v1/yourdomain.com" | jq '.data.registration_status'
# Expected: "registered"

# 4. Verify the custom domain serves your canister
curl -sI "https://yourdomain.com"
# Expected: HTTP/2 200

# 5. Verify no stale ACME TXT records
dig TXT _acme-challenge.yourdomain.com
# Expected: no TXT records (only the CNAME)

コメント