アプリ

ディープリンクとは?ユニバーサルリンク・URLスキーム・App Linksの違いと実装手順

ディープリンクとは?ユニバーサルリンク・URLスキーム・App Linksの違いと実装手順

ディープリンクは、リンクをタップした人をアプリのトップではなく、商品詳細や記事など特定の画面へ直接届ける仕組みです。ユニバーサルリンクとの違いは「目的」と「方式」の関係にあります。ディープリンクが目的の名前で、ユニバーサルリンクはそれをiOSで実現するための、通常のhttps://リンクを使う方式の一つです。

方式はほかに、独自スキーム(myapp://)で起動するカスタムURLスキームと、Androidの App Links があります。カスタムURLスキームは「廃止」と言われがちですが、OSから消えたわけではありません。この記事では、方式の違い、URLスキームの現在の扱い、アプリで開かない条件、iOS・Androidの設定と受け取りコードを、Apple・Googleの公式ドキュメントに沿って整理します。

まとめ

  • ディープリンクとユニバーサルリンクの違い:ディープリンクは「アプリ内の特定画面を開く」という目的の総称です。ユニバーサルリンクはそれをiOSで実現する方式で、Webと同じhttpsのURLを使い、アプリが無ければそのままWebページが開きます。
  • カスタムURLスキームは廃止されていません。Appleは「許容される方式」としつつユニバーサルリンクを強く推奨しています。同じスキームを複数のアプリが登録すると、どのアプリが開くかは未定義です。
  • App LinksはAndroid版の同等機能で、assetlinks.jsonで検証されたドメインのリンクだけを選択ダイアログなしでアプリに渡します。Android 12以降は、未検証のWebリンクは既定のブラウザで開きます。
  • ユニバーサルリンクは、Safariで同じドメインのリンクをタップしたときや、自分のアプリから自ドメインを開いたときはアプリに切り替わりません。動かないときはまずこの仕様を疑います。
  • Firebase Dynamic Links は2025年8月25日に終了し、クリックは404を返します。OS標準の方式を基本とし、遅延ディープリンクや計測が必要なら、自前の追加実装と外部サービスの利用を比較します。

ディープリンクの意味:アプリの特定画面を開くURL

ディープリンクは、利用者をアプリのトップ画面ではなく目的の画面へ直接届けるリンクです。OSがURLを解釈して対応アプリを起動し、アプリはURLのパスやクエリから「どの画面を、どのIDで開くか」を決めます。https://example.com/product/123を開けば商品123の詳細が表示される、という動きです。

効くのは、遷移先がはっきり決まっている導線です。メールやSNS、広告から商品詳細へ、プッシュ通知から該当の予約画面へ、といった使い方では、トップから探させる手順がそのまま離脱の原因になります。

Web文脈の「ディープリンク」との違い

Webの世界では、サイトのトップではなく下層ページへ直接張るリンクもディープリンクと呼びます。こちらはHTMLの普通のリンクで、アプリの起動とは関係がありません。内部リンクの設計という意味での話題はSEO内部対策の解説で扱っています。なお、アプリのディープリンクは遷移先を制御する仕組みであり、設定だけでGoogle検索の順位向上を保証するものではありません。検索結果からアプリを開かせる仕組みはApp Indexingの記事にまとめています。

ディープリンクとユニバーサルリンク・URLスキーム・App Linksの違い

「ディープリンクとユニバーサルリンクのどちらを使うか」という比較は成り立ちません。選ぶ対象は、ディープリンクを実現する3つの方式(カスタムURLスキーム、ユニバーサルリンク、App Links)で、違いは次の表のとおりです。

観点 カスタムURLスキーム ユニバーサルリンク App Links
対応OS iOS・Android iOS 9以降・macOSなど Android 6以降(Google サービス搭載端末)
URLの形 myapp://product/123 https://example.com/product/123 https://example.com/product/123
アプリ未インストール時 何も起きない・エラー 同じURLのWebページが開く 同じURLのWebページが開く
所有の証明 なし(誰でも登録可) apple-app-site-association assetlinks.json
他アプリの横取り 起こりうる 起こらない 起こらない

決定的な差は「ドメインの所有を証明しているか」です。ユニバーサルリンクとApp Linksは、Webサーバーに置いた設定ファイルとアプリの設定が双方向で一致したときだけ、OSがそのドメインのリンクをアプリへ渡します。カスタムURLスキームにはこの確認がないため、同じ名前を名乗るアプリがあれば誤ったアプリが開きます。

ユニバーサルリンクとカスタムURLスキームの挙動差

Appleのユニバーサルリンクの解説によると、ユニバーサルリンクをタップするとブラウザやWebサイトを経由せずにアプリが開き、アプリが入っていなければ既定のブラウザでそのURLが開きます。リンクは1本で、Webとアプリの両方に使えます。カスタムURLスキームは、アプリが無い端末では開く先がありません。メールや広告に貼るリンクとしては、ここが致命的な差になります。

App Links(Android)とユニバーサルリンクの共通点と相違点

仕組みはほぼ同じで、httpsのURLとWebサーバー上の検証ファイルを使います。相違点は検証の中身です。Androidのassetlinks.jsonにはアプリの署名証明書のSHA-256フィンガープリントを書き、OSはそれがインストールされたアプリの署名と一致するかを確かめます。AndroidのApp Linksの解説は対応範囲を「Android 6(API 23)以降、Google サービスを搭載した端末」としており、Google サービスの無い端末では従来どおりのディープリンク扱いになります。

Androidにはもう一つ、検証していないhttpsリンク(Web links)があります。Android 12(API 31)以降、アプリがそのドメインで承認されていなければ、Webリンクは既定のブラウザで開きます。intent-filterにhttpsを書いただけではアプリは開きません。

静的・動的・遅延ディープリンクの使い分け

方式とは別に、リンクの作り方による分類もあります。判断の軸は「アプリが無い人をどう扱うか」と「流入を計測するか」の2つです。

種類 中身 未インストール時 向く用途
静的 URLが固定 方式に依存 常設の導線・アプリ内共有
動的 生成時にパラメータを付与 サービスの設定に従う 広告・キャンペーン
遅延(ディファード) インストール後に元の遷移先を復元 ストア経由で目的画面へ 新規獲得の広告

OS標準のユニバーサルリンクとApp Linksは、インストール済みの端末でアプリを開く仕組みです。ストアを経由したあとに元の画面へ運ぶ遅延ディープリンクは含まれません。広告から新規インストール後に特定の商品画面へ誘導するには、遷移先を引き継ぐ追加実装が必要です。外部サービスを利用する方法のほか、AndroidではGoogle Play Install Referrerを使って自前で実装する方法もあります。既存ユーザー向けのメールや通知が中心なら、静的なユニバーサルリンクとApp Linksで足ります。

カスタムURLスキームは廃止されたのか:Appleの推奨とiOSの制限

廃止はされていません。AppleのカスタムURLスキームの定義方法には「While custom URL schemes are an acceptable form of deep linking, universal links are strongly recommended.」とあり、許容される方式だがユニバーサルリンクを強く推奨する、という位置づけです。カスタムURLスキームへの対応、ユニバーサルリンクの推奨、問い合わせAPIの制限・非推奨化は、それぞれ別の話です。

カスタムURLスキームの弱点:競合・入力検証・Webとの関連付け

  • 横取り:識別子に逆ドメイン形式を使っても、ほかのアプリが同じスキームを登録することは防げません。複数のアプリが同じスキームを登録した場合、どのアプリが開くかは未定義で、変更する手段もありません。
  • 攻撃の入口:「URL schemes offer a potential attack vector into your app」と明記され、すべてのパラメータの検証、不正な形式のURLの破棄、データ削除や機微情報へのアクセスを外部から許さないことが求められています。
  • Webとの一体性がない:同じドキュメントは、Webサイトと一意に結びついたリンクを定義するならユニバーサルリンクを使うよう案内しています。

canOpenURLの件数制限とiOS 27での非推奨化

他アプリのスキームを開けるか確かめるcanOpenURL(_:)は、段階的に締め付けられてきました。AppleのcanOpenURL(_:)のリファレンスによると、iOS 9.0以降でリンクしたアプリは問い合わせるスキームをInfo.plistのLSApplicationQueriesSchemesに宣言しなければならず、宣言していないスキームには常にfalseが返ります。宣言できる数は、iOS 15以降のSDKでリンクしたアプリで最大50件、iOS 27以降のSDKでリンクしたアプリで最大25件です。さらに、iOS 27のリリースノートでcanOpenURL(_:)自体が非推奨になりました。Appleは事前確認の代わりにURLを開いて失敗を処理する方法、またはユニバーサルリンクへの移行を案内しています。

Androidと認可フロー(OAuth)での扱い

Androidでもカスタムスキームは使えますが、公式ドキュメントは、同じスキームを別アプリが登録すると選択ダイアログが出うると説明しています。ログインの戻り先(リダイレクトURI)にカスタムスキームを使う場合は、横取り対策が必須です。ネイティブアプリのOAuthを定めたRFC 8252は、カスタムスキームでは複数のアプリが同じスキームを登録でき、認可コードがどのアプリに届くか定まらないと指摘し、iOS 9以降ではユニバーサルリンクによるhttpsの戻り先を推奨しています。カスタムスキームを使うならPKCEを必ず組み合わせます。

判断としては、アプリ内の画面遷移や自社アプリ間の連携など、送り手と受け手を自分で管理できる用途ならカスタムURLスキームを残して構いません。メール・広告・SNSなど外へ配るリンクは、ユニバーサルリンクとApp Linksに置き換えます。

ユニバーサルリンク・App Linksがアプリで開かない8つの条件

設定後もWebが開く原因には、OSやブラウザの仕様によるものと、検証ファイルや署名の設定不備によるものがあります。公式ドキュメントに書かれている条件を先に確認します。

  1. Safariで同じドメインのリンクをタップした:Appleは、Safariで自サイトを見ている人が同じドメインのユニバーサルリンクをタップした場合はSafariのまま開く、と説明しています。別ドメインのリンクならアプリが開きます。自社サイトのページからアプリへ渡したいリンクは、別のサブドメインに置くと切り替わります。
  2. 自分のアプリから自分のドメインを開いた:アプリ自身がopen(_:options:completionHandler:)やSwiftUIのopenURLで自ドメインのユニバーサルリンクを開いても、そのアプリには戻りません。
  3. アドレス欄にURLを直接入力した:Appleのデバッグ手引きTN3155は、ブラウザのアドレス欄への直接入力はブラウザ内の移動なので、アプリは決して開かないと説明しています。動作確認はメモアプリに貼ったリンクから行います。
  4. 以前に「Safariで開く」を選んだ:リンクを長押しして表示される選択肢で選んだ動作は、そのドメインのユニバーサルリンクの既定になります。戻すには同じ手順でアプリを選び直します。
  5. 設定ファイルがリダイレクトやHTTPで配信されている:apple-app-site-associationは拡張子なしで、有効な証明書のhttpsからリダイレクトなしで配信する必要があります。assetlinks.jsonもhttps・リダイレクトなし(301・302不可)・content-type: application/jsonが条件です。wwwありなしの転送ルールに巻き込まれる事故が典型です。
  6. Appleのキャッシュが古い:iOS 14・macOS 11以降、端末はWebサーバーではなくApple管理のCDNからapple-app-site-associationを取得します。サーバーを直してもすぐに反映されないことがあります。
  7. Android 11以下で一部のホストだけ検証に失敗した:Android 11(API 30)以下は、マニフェストに書いた全ホストでassetlinks.jsonが一致したときだけ既定のハンドラーになります。1ホストの失敗で全体が開かなくなります。
  8. Androidの署名フィンガープリントが違う:Play App Signingを使っていると、手元でkeytoolから出した値は利用者の端末に入るアプリの署名と通常一致しません。Play Consoleの[リリース]→[設定]→[アプリの署名]に正しい値が載っています。

Safari以外のブラウザでユニバーサルリンクを有効にするかは各ブラウザの実装次第で、TN3155もブラウザの提供元に確認するよう案内しています。

iOSの実装手順:AASAの書き方と受け取りコード

手順は3つです。Webサーバーにapple-app-site-associationを置く、XcodeでAssociated Domainsを設定する、アプリでURLを受け取る、の順に進めます。

apple-app-site-associationのcomponents記法

ファイルはhttps://<ドメイン>/.well-known/apple-app-site-associationに置きます。現在の書式はappIDsとcomponentsです(古いappID・paths形式も後方互換で動きます)。サブドメインごとにファイルとentitlementの登録が必要です。

{
  "applinks": {
    "details": [
      {
        "appIDs": ["ABCDE12345.com.example.app"],
        "components": [
          { "/": "/product/*/review", "exclude": true },
          { "/": "/product/*" }
        ]
      }
    ]
  }
}

componentsは配列の上から順に照合され、最初に一致した規則が採用されます(Appleのcomponents のリファレンスの「The first match wins」)。除外したいパスは、それを含む広い規則より前に書きます。上の例で除外を2行目に置くと、レビュー画面のURLも先に/product/*に一致してアプリで開きます。

Associated DomainsとAppleのCDN

Xcodeの[Signing & Capabilities]でAssociated Domainsを追加し、applinks:example.comの形でドメインを登録します。パスや末尾のスラッシュは書きません。iOS 14以降、端末はApple管理のCDNからファイルを取得し、CDNに古い版しか無いときやコピーが無いときはCDNがWebサーバーへ最新版を取りに行きます。CDNが持っている内容は次のURLで確認できます。

curl -s https://app-site-association.cdn-apple.com/a/v1/example.com

開発中の非公開サーバーを使う場合は、entitlementをapplinks:dev.example.com?mode=developerとします。開発用プロビジョニングプロファイルで署名したアプリに限られ、端末側でも[設定]→[デベロッパ]のAssociated Domains Developmentを有効にすると、CDNを迂回してサーバーへ直接取りに行きます。同じ画面のDiagnosticsにURLを入力すると、インストール済みのアプリで有効なリンクかを判定できます。Macではsudo swcutil dl -d example.comで、AASAを取得できるかを確かめられます。

アプリでURLを受け取るコード(SwiftUI・UIKit)

SwiftUIのライフサイクルなら、onOpenURLでユニバーサルリンクもカスタムURLスキームも受け取れます。

@main
struct ShopApp: App {
    @State private var path: [Int] = []

    var body: some Scene {
        WindowGroup {
            ContentView(path: $path)
                .onOpenURL { url in
                    // https://example.com/product/123 を想定
                    guard url.scheme == "https",
                          url.host == "example.com",
                          url.pathComponents.count == 3,
                          url.pathComponents[1] == "product",
                          let id = Int(url.pathComponents[2]) else { return }
                    path = [id]
                }
        }
    }
}

UIKitのシーンでは、ユニバーサルリンクはNSUserActivityとして届きます。起動中・バックグラウンドからの復帰ではscene(_:continue:)、未起動からの起動ではscene(_:willConnectTo:options:)のconnectionOptions.userActivitiesに入るため、両方で同じ処理を呼びます。iOS 27のリリースノートは、最新のSDKでビルドしたアプリはシーンベースのライフサイクルを採用しないと起動しないとしているため、AppDelegateだけで受け取る古い実装は移行が必要です。Appleはユニバーサルリンクの受け取り方でも、URLスキームと同じくパラメータをすべて検証し、不正な形式のURLは捨てるよう求めています。シーンの仕組みはUIKitとSwiftUIの比較記事で扱っています。

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
          let url = userActivity.webpageURL,
          let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
        return
    }
    // components.path と components.queryItems から遷移先を決める
    router.open(path: components.path, query: components.queryItems ?? [])
}

Androidの実装手順:intent-filterとassetlinks.json

Androidは、マニフェストで対象のURLを宣言し、Webサーバーで署名を証明する二段構えです。

intent-filterとassetlinks.jsonの書き方

App Linksにするにはandroid:autoVerify="true"を付け、VIEW・DEFAULT・BROWSABLEの3つを揃えます。DEFAULTが欠けると、検証が通っても暗黙インテントで起動されません。

<activity android:name=".ProductActivity" android:exported="true">
  <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="example.com" android:pathPrefix="/product/" />
  </intent-filter>
</activity>

Webサーバーにはhttps://example.com/.well-known/assetlinks.jsonを置きます。sha256_cert_fingerprintsには複数の値を書けるので、デバッグ用と本番用を並べられます。値の出し方と注意点はAndroidのassetlinks.jsonの設定ガイドにあります。

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example.app",
    "sha256_cert_fingerprints": ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
  }
}]

アプリ側では、新しいActivityが作られる場合はonCreateでURLを読みます。起動モードやIntentのフラグによって既存のActivityが再利用される場合に備え、onNewIntentでも同じ処理を呼びます。

class ProductActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleLink(intent)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        handleLink(intent)
    }

    private fun handleLink(intent: Intent) {
        val uri = intent.data ?: return
        // https://example.com/product/123 の形だけを受け付け、IDが数字でなければ捨てる
        if (intent.action != Intent.ACTION_VIEW ||
            uri.scheme != "https" || uri.host != "example.com") return
        val segments = uri.pathSegments
        if (segments.size != 2 || segments[0] != "product") return
        val id = segments[1].toLongOrNull() ?: return
        showProduct(id)
    }
}

検証状態を確かめるadbコマンド

Android 12以降の端末では検証を手動でやり直せます。掲載コマンドはtargetSdkVersionが31以上のアプリを想定しています。31未満では、先にadb shell am compat enable 175408749 com.example.appで新しい検証処理を有効にします。手順はAndroidのApp Linksの検証ガイドのとおりです。

# 状態をリセットしてから再検証する
adb shell pm set-app-links --package com.example.app 0 all
adb shell pm verify-app-links --re-verify com.example.app
# 数分待ってから結果を見る
adb shell pm get-app-links com.example.app

ドメインの状態がverifiedなら成功です。legacy_failureは旧来の検証で拒否されたこと、noneは検証の記録が無いこと、1024以上の数値は端末側の検証エラーを表します。Android 15(API 35)以降では、assetlinks.json側で対象パスを後から変えられるDynamic App Linksも使えますが、Android 14以下では無視されるため、除外の規則は旧OSでは効きません。

Firebase Dynamic Links終了後の選び方

動的ディープリンクの定番だったFirebase Dynamic Links(FDL)は、FirebaseのFAQのとおり2025年8月25日に終了しました。独自ドメインとpage.linkのどちらのリンクもクリックするとHTTP 404を返し、新規作成もできません。移行先は2択です。

  • OS標準(ユニバーサルリンク+App Links):リンク機能そのものに利用料はかかりませんが、開発・配信基盤・保守の費用は別途必要です。遅延ディープリンクと流入計測も追加で実装します。既存ユーザー向けの共有・メール・通知が中心ならこれで足ります。
  • 外部のディープリンク・計測サービス:FirebaseのFAQは、FDLと同等の機能が要る場合の移行先としてAdjust、Airbridge、AppsFlyer、Bitly、Branch、Kochava、Singularなどを挙げています(Googleが審査したものではないと注記付き)。対応機能、SDKの組み込み要否、料金はサービスや契約プランによって異なるため、遅延ディープリンクとインストール計測の要件に照らして確認します。

広告経由のインストール計測が要る導線だけを外部サービスに寄せ、残りはOS標準にする組み合わせが費用を抑えやすい構成です。他社のアプリ・WebサイトをまたぐトラッキングやIDFAへのアクセスにはiOSのATT(トラッキング許可)が必要になるため、広告識別子(IDFA・AAID)の扱いも同時に設計します。旧短縮リンクの棚卸しなど移行作業の手順はFirebase Dynamic Linksの終了と移行の記事にまとめています。iOS・Android両方のアプリでリンクの受け口を揃える開発は、一創のスマホアプリ開発でも設計から対応しています。

よくある質問

ディープリンクとは何ですか?

リンクをタップしたときに、アプリのトップではなく商品・記事・予約画面などの特定の画面を直接開く仕組みです。iOSではユニバーサルリンク、AndroidではApp Links、両OS共通ではカスタムURLスキームで実現します。

ディープリンクとユニバーサルリンクの違いは?

ディープリンクは「アプリ内の特定画面を開く」という目的の総称で、ユニバーサルリンクはそれをiOSで実現する方式の一つです。ユニバーサルリンクはhttpsのURLを使い、ドメインの所有をapple-app-site-associationで証明するため、アプリが無い端末ではWebページが開き、他アプリに横取りされません。

カスタムURLスキームは廃止されたのですか?

廃止されていません。Appleは許容される方式としたうえでユニバーサルリンクを強く推奨しています。ただし、canOpenURL(_:)で問い合わせられるスキームはiOS 15以降のSDKでリンクしたアプリでは最大50件、iOS 27以降のSDKでリンクしたアプリでは最大25件です。これは宣言できるスキーム数の制限であり、URLを開く回数の制限ではありません。また、iOS 27ではこのAPI自体が非推奨です。

Androidでディープリンクを設定する方法は?

マニフェストのintent-filterにandroid:autoVerify="true"とVIEW・DEFAULT・BROWSABLE、httpsのホストを書き、Webサーバーの/.well-known/assetlinks.jsonに署名のSHA-256フィンガープリントを置きます。検証結果はadb shell pm get-app-linksで確認できます。

Firebase Dynamic Linksはまだ使えますか?

使えません。2025年8月25日に終了し、既存のリンクは404を返します。OS標準のユニバーサルリンクとApp Linksへ移し、遅延ディープリンクが要る導線だけ外部サービスか自前の実装で補います。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.09 テックブログ エディオン不正アクセス第2報:カード番号の対象確認とログ・決済画面の改ざん対策
  2. 2026.11.09 テックブログ LANSCOPE不正アクセス第2報:製品への影響と偽連絡の確認、停止中のライセンス認証の窓口
  3. 2026.11.10 テックブログ Claude Fable 5.2とは:料金・破壊的変更4点とFable 5.1からの移行手順【2026年11月】
  4. 2026.11.10 テックブログ ニトリの情報漏洩:最大3,762万件の対象確認となりすまし連絡の見分け方
  5. 2026.11.09 テックブログ ステップゴルフ不正アクセス:口座情報の対象確認と偽連絡・引き落としの注意点

RELATED POSTS 関連記事

目次