universal links 設定:iOSとAndroidのウェブ側

universal links 設定の手順。apple-app-site-associationとassetlinks.jsonの置き方と確認方法を解説します。

目次

universal links 設定は、iOSのUniversal linksとAndroidのApp Linksで、ユーザーが通常のhttps://リンクをタップしたときに、ウェブサイトではなくアプリを開きます。ウェブサイト側の作業は、自分でホストする2つのJSONファイルです。iOS用のapple-app-site-associationとAndroid用のassetlinks.jsonです。両方ともアプリが扱うすべてのドメインの/.well-known/に置き、HTTPSでリダイレクトなしに配信する必要があります。どちらのファイルも、URLを開いてよいアプリを指定します。アプリ側にも対応する設定(iOSはエンタイトルメント、Androidはインテントフィルター)が必要です。それぞれのファイルの隣に示します。

universal linksとAndroid App Linksの仕組み

どちらの仕組みも、ウェブサイトとアプリを双方向につなぎます。アプリが扱うドメインを宣言し、ウェブサイトがそれを確認します。Appleは、universal linksを通常のHTTPまたはHTTPSリンクとして説明しています。そのため、1つのURLでサイトとアプリの両方に対応でき、アプリがないユーザーにはブラウザでページが表示されます(Apple)。Googleによると、Android App Linksは通常のディープリンクと同じインテントの仕組みを基にしていますが、ウェブサイト上のDigital Asset Linksファイルによる検証のステップが加わります(Android Developers)。

iOSのuniversal links AndroidのApp Links
ウェブサイト上のファイル apple-app-site-association assetlinks.json
配置場所 https://<ドメイン>/.well-known/apple-app-site-association https://<ドメイン>/.well-known/assetlinks.json
アプリの宣言 Associated Domainsのエンタイトルメント、applinks:<ドメイン> android:autoVerify="true"付きのインテントフィルター
アプリの識別子 Team IDとbundle ID パッケージ名と署名証明書のフィンガープリント
検証のタイミング インストール時。Appleの CDN経由 インストール時。Android 15以降はバックグラウンドでも
ユニバーサルリンクとApp Linksは両側の設定が必要:アプリがドメインを宣言し(Associated DomainsまたはautoVerify付きインテントフィルター)、サイトが.well-knownにファイル(apple-app-site-associationまたはassetlinks.json)を置く

「ディープリンク」はより広い用語です。通常のディープリンクはカスタムスキームや、検証されていないhttps://リンクを使うことがあります。universal linksとApp Linksは、検証された種類のものです。

iOS用のファイル:apple-app-site-association

拡張子のないapple-app-site-associationというファイルを作成し、アプリのIDと、アプリで開くべきURLパターンを列挙します。以下はAppleのドキュメントにある構造を簡略化したものです。

{
  "applinks": {
    "details": [
      {
        "appIDs": ["ABCDE12345.com.example.app"],
        "components": [
          { "/": "/buy/*", "comment": "Open URLs that start with /buy/ in the app." },
          { "/": "/help/website/*", "exclude": true, "comment": "Keep these in the browser." }
        ]
      }
    ]
  }
}

appIDsの値は、アプリIDのプレフィックス(Team ID)、ドット、bundle identifierを連結したものです。componentsの各項目はURLの一部に一致し、"exclude": trueは一致するURLをアプリで開かないようにシステムに指示します(Apple、Supporting associated domains)。AppleはappIDとpathsを使う古い形式も受け付けますが、その技術ノートでは2つを混在させないよう勧めています(TN3155)。

https://<完全修飾ドメイン名>/.well-known/apple-app-site-associationで、有効な証明書を使い、リダイレクトなしで配信してください。Appleのページにある3つのルールで、つまずきやすい点があります。

  • サブドメインごとにアプリ側のエントリが必要です。example.comのファイルはapplinks:example.comとapplinks:*.example.comに対応しますが、www.example.comのような特定のサブドメインには専用のファイルが必要です。
  • ファイルは、どのIPアドレスからでも、どのユーザーエージェントからでも到達できる必要があります。ファイアウォール、ボットフィルター、ログインウォールから403や404が返ると、Appleのサーバーはファイルをキャッシュできません。
  • iOS 14以降、端末はファイルをあなたのサーバーから取得しません。AppleのCDNが取得してキャッシュし、端末は約1週間に1度更新を確認します。このキャッシュを直接無効化する方法はありません。アプリを再インストールすると新しいコピーが取得されます(TN3155)。

開発中は、エンタイトルメントのエントリに?mode=developerを追加できます(applinks:example.com?mode=developer)。そうすると、端末はAppleのCDNではなく、あなたのサーバーから直接ファイルを読みます。

Android用のファイル:assetlinks.json

assetlinks.jsonは、パッケージ名とアプリに署名する証明書のSHA-256フィンガープリントを指定するステートメントの配列です。以下はGoogleの、1つのアプリの例です。

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example",
    "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"]
  }
}]

パッケージ名とフィンガープリントは自分のものに置き換えてください。要件はGoogleのガイド(2026年10月23日更新)による次のとおりです。

  • https://<ドメイン>/.well-known/assetlinks.jsonで、コンテンツタイプはapplication/jsonにして配信する。
  • アプリのインテントフィルターがhttpも宣言していても、HTTPSを使う。
  • 301でも302でも、リダイレクトしない。
  • アプリが扱うホストドメインのすべてに公開する。
  • 1つのファイルに複数のアプリを列挙でき、sha256_cert_fingerprintsフィールドには複数のフィンガープリントを指定できます(デバッグビルドと本番ビルドなど)。

Play App Signingを使っている場合、手元のkeytoolで得たフィンガープリントは、ユーザーの端末のものと一致しないのが普通です。Googleは、Play Consoleの Release > Setup > App signing から正しいJSONの断片を取得するよう勧めています。

アプリ側は、autoVerify付きのインテントフィルターです。Androidのドキュメントにある形式は次のとおりです。

<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="http" />
    <data android:scheme="https" />
    <data android:host="www.example.com" />
</intent-filter>

Androidはそのフィルターにあるホスト名をそれぞれ検証します。*.example.comのようなワイルドカードのホストでは、ルートドメインのexample.comにファイルが必要です(Android Developers)。Android 15以降では検証は定期的にも行われ、ファイルの変更が反映されるまで最大7日かかることがあります。Android 14以前では、インストールかアップデートの時にだけファイルが読まれます(Android Developers)。

サーバーのチェックリスト

どちらのファイルも同じ少数の理由で失敗するので、サーバーは一度にまとめて確認してください。

確認項目 理由 確認方法
HTTPS 両プラットフォームとも必須。Appleは有効な証明書を求める ブラウザでURLを開く
301も302もない どちらのファイルもリダイレクトに対応していない curl -sIで200が返る
iOS:すべてのIPとユーザーエージェントに公開 AppleのCDNがファイルを取得する。ファイアウォールやボットフィルターがブロックしていないこと curl -A "MyAgent-Bot/1.0" <url>でファイルが返る
Android:Content-Type: application/json Googleが要件として挙げている curl -sIのヘッダー
列挙したすべてのドメインとサブドメインに同じファイル ホストごとに個別に検証される ホストごとに確認する

コンテンツタイプについては、両方のファイルをapplication/jsonで配信すると設定が統一されます。Appleのドキュメントは、iOSのファイルに対するコンテンツタイプを示していないので、その部分は自分で選ぶことになります。静的なnginxの例は次のとおりです。

location = /.well-known/apple-app-site-association {
    default_type application/json;
}
location = /.well-known/assetlinks.json {
    default_type application/json;
}

動作の確認方法

まず外部からファイルを確認し、その後で端末上の動作を確認します。以下はAppleのデバッグ手順(TN3155)です。

  1. curl -v https://<ドメイン>/.well-known/apple-app-site-associationを実行し、200とJSONが返ることを確認します。301、302、403、404ではないことも確認してください。
  2. Macでsudo swcutil dl -d <ドメイン>を実行し、ファイルがダウンロードできるかを確認します。sudo swcutil verify -d <ドメイン> -j <JSONのパス> -u <URL>を使うと、URLがパターンに一致するかを確認できます。
  3. iPhoneでは、リンクをメモアプリに貼り付けて長押しします。すべてが正しく設定されていれば、アプリで開くオプションとブラウザで開くオプションの両方が表示されます。

Androidでは、Googleの手動検証を使います。Android 12以降の場合は次のとおりです。Android 12より前を対象にしたアプリの場合は、まずadb shell am compat enable 175408749 com.exampleを実行してください。

adb shell pm set-app-links --package com.example 0 all
adb shell pm verify-app-links --re-verify com.example
# 数分待ってから:
adb shell pm get-app-links com.example

出力には、各ドメインがverifiedやlegacy_failureなどの状態とともに表示されます。Googleは既存のステートメントファイルをテストするためのDigital Asset Linksのツールも提供しています。より広いテストの流れについては、モバイルサイトのテストチェックリストを参照してください。

リンクでアプリが開かない場合

iOSでは、ブラウザのアドレスバーにURLを入力してもアプリは開きません。これは直接ナビゲーションだからです。ユーザーがすでに自分のドメインを見ている場合、同じドメインへのリンクもSafari内にとどまります(TN3155)。ログインボタンのような流れでは、Appleの解決策は別のサブドメインを使うことです。そのサブドメインには独自のapplinksエントリとAASAファイルを用意し、システムがそのタップをサイトからの離脱として扱うようにします。

アプリ内ブラウザは、別途テストが必要です。Appleは、ブラウザアプリやWKWebView内でタップされたリンクはアプリを開けると述べていますが、同時にサードパーティのブラウザがuniversal linksを有効にするかどうかは各ブラウザが決めるとしています(「universal linksはすべてのウェブブラウザで有効になるとは限りません」、TN3155)。また、アプリが自身のopen(_:)呼び出しでリンクを開く場合、そのアプリは逆方向のリンクを開かないとも述べています(Apple)。特定のSNSアプリのブラウザがリンクをアプリに渡すかどうかはプラットフォームのドキュメントにはないため、重要なアプリは実機で個別にテストしてください。

フォールバックとしてのスマートアプリバナー

スマートアプリバナーは、iOS専用のウェブページ上の通知です。表示したいページの<head>にメタタグを1つ置きます。Appleによると、アプリがインストールされていればアプリを開き、なければApp Storeへのリンクを表示します。

<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-argument=https://example.com/buy/42">

app-idは必須です。省略可能なapp-argumentは、対応する画面を開けるようにURLをアプリに渡します。Appleは2つの制限を挙げています。バナーはフレーム内には表示されず、iOSシミュレーターにも表示されません。

ネイティブアプリを提供していなければ、どちらのファイルも不要です。ブラウザからインストールできるプログレッシブウェブアプリが代わりになります。

スマートフォン幅でフォールバックページを確認する

アプリを持っていない人や、アプリに引き渡されなかったリンクは、すべてウェブページにたどり着きます。そのためページはスマートフォンでも動く必要があります。Mobile View Extensionは、タブをスマートフォンのユーザーエージェント、画面サイズ、ピクセル比で読み込み直します。フォールバックページの実際のモバイル版を複数のサイズで確認でき、Chromeのデバイスエミュレーションと組み合わせることもできます。ただし、アプリへの引き渡し自体はテストできません。デスクトップ版Chromeで動き、タッチイベントもインストールされたアプリもないからです。リンクの動作は、iPhoneかAndroidの実機、またはiOSシミュレーターかAndroidエミュレーターで確認してください。iOSの実機がない場合は、iPhoneがなくてもiPhone向けにウェブサイトをテストする方法を参照してください。

よくある質問

universal linksとディープリンクの違いは何ですか?

ディープリンクはより広いカテゴリで、universal linksはそのうちAppleの検証済みのものです。標準のHTTPまたはHTTPSリンクを基にしています。ディープリンクは、自分が管理していないウェブサイトとは関係のないカスタムURLスキームを使うこともあります。AndroidのApp Linksは、通常のディープリンクにDigital Asset Linksの検証を加えた、Android上の検証済みの形式です。

apple-app-site-associationはどこに置きますか?

https://<ドメイン>/.well-known/apple-app-site-associationです。拡張子は付けず、HTTPSで有効な証明書を使い、リダイレクトなしで配信します。アプリが列挙する各サブドメインには、それぞれのコピーが必要です。

universal linkを開いてもアプリではなくSafariが開くのはなぜですか?

よくある原因は、リダイレクト、ファイルでの403や404、サブドメインのエントリ不足、同じドメインへのタップです。Appleは TN3155 でこれらを挙げています。Safariで閲覧中に、アドレスバーにURLを入力したり、同じドメインへのリンクをタップしたりすると、ユーザーはブラウザにとどまります。まずcurl -vでファイルを確認してください。

両方のファイルが必要ですか?

アプリを公開しているプラットフォームごとに、そのファイルが必要です。iOSアプリとAndroidアプリの両方を持つサイトは両方を配信し、それぞれは互いに独立しています。

次のサイトをモバイル表示で確認しましょう。

拡張機能を追加して、作業中のタブのまま端末プレビューを開けます。