[Android] App Links(Digital Asset Links)の検証が失敗する原因と対処法 ― assetlinks.jsonとステータス確認

自社ドメインのリンクをタップしたら、当然アプリが開くと思っていたのに――なぜかブラウザで開いてしまう。App Links(Digital Asset Links)の検証まわりで、こんな状況に頭を抱えたことはありませんか?

この機能の厄介なところは、失敗してもユーザー向けのエラーが一切出ない点です。設定は合っているように見えるのに、リンクが静かにブラウザへ流れていく。原因が見えないので、切り分けに何日も溶かしてしまいがちなんですよね。

この記事では、App Links の検証が失敗する原因を「起きやすい順」に整理し、assetlinks.json の確認方法から adb でのステータス確認まで、実際に手を動かして切り分ける手順をまとめていきます。

スポンサーリンク

まず、検証の仕組みをざっくり押さえる

App Links の自動検証は、おおまかに次の流れで動きます。

  1. intent-filter に android:autoVerify="true" があると、インストール時に Android が対象ホスト名を集める
  2. 各ホストの https://ホスト名/.well-known/assetlinks.json を取りに行く
  3. ファイル内の sha256_cert_fingerprints と、アプリの署名証明書のフィンガープリントを照合する
  4. 一致すれば「検証済み」。以降そのドメインのリンクはアプリで直接開く

ここで重要なのが、不一致や取得失敗が起きても、ユーザーに見えるエラーは出ないということ。Android 12 以降は、検証に失敗したリンクは黙ってブラウザで開かれます(11 以前は選択ダイアログが出ていたので、まだ異変に気づけました)。この「無言の失敗」こそが、原因究明を難しくしている元凶です。

assetlinks.json の正しい形

まず基本形を確認しておきましょう。https://あなたのドメイン/.well-known/assetlinks.json に、次の内容を置きます。

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.sampleapp",
      "sha256_cert_fingerprints": [
        "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:..."
      ]
    }
  }
]

マニフェスト側は、対象の Activity にこんな intent-filter を書きます。

<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="sample.com" />
</intent-filter>

原因1:SHA-256フィンガープリントの不一致(最頻)

本番で検証が失敗するケースのほとんどがこれです。特に Play App Signing を使っていると、ここでハマります。

Play App Signing では、Google がアップロード鍵とは別の鍵であなたのアプリを再署名してユーザーに配信します。つまり端末上のアプリの署名は、あなたのローカルの鍵ではなく Google が管理する「アプリの署名鍵」です。

そのため assetlinks.json に書くべきは、Play Console の「アプリの署名」(App integrity)に表示される署名鍵の SHA-256 であって、手元のデバッグ鍵やアップロード鍵のフィンガープリントではありません。ここを取り違えると、デバッグビルドでは検証が通るのに、Playからの本番ビルドで静かに壊れるという、いちばん気づきにくい形で失敗します。

あわせて、フィンガープリントは大文字(アッパーケース)で書く必要があります。小文字だと一致しません。

原因2:サーバー側でファイルが取得できない(FETCH_ERROR系)

SHA-256 が合っていても、そもそもファイルを Android(Google の検証基盤)が取得できなければ検証は失敗します。ログに ERROR_CODE_FETCH_ERROR のような取得エラーが出ている場合は、この系統を疑います。よくある原因は次のとおりです。

  • リダイレクト:301 / 302 は検証を即失敗させます。www への付け替えや末尾スラッシュの付与が、いちばん多いサーバー側原因です。直接 200 で返す必要があります。
  • Content-Typeapplication/json でなければなりません。text/plaintext/html で返していると弾かれます。
  • 認証・bot保護:Basic認証、ログイン壁、WAFやCDNのbotチャレンジが .well-known をブロックしていると取得できません。この経路は公開されている必要があります。
  • 証明書:HTTPS証明書が有効で信頼できるものである必要があります。自己署名や期限切れは失敗します(ブラウザのような「無視して続行」はありません)。
  • ジオブロッキング:地域やアクセス元でコンテンツを出し分け・遮断していると、検証基盤からのアクセスだけ弾かれることがあります。特定地域からだけ取得できない、という形で表面化します。

まずは curl で、素直に 200 が返るかを確認するのが第一歩です。

# ヘッダーを確認:200か / Content-Typeは何か
curl -sI https://sample.com/.well-known/assetlinks.json
 
# リダイレクトの連鎖を丸ごと見る
curl -sIL https://sample.com/.well-known/assetlinks.json

原因3:package_name違い・複数ホストの巻き込み

package_name が実際のアプリと違っている、という単純ミスも意外とあります。あわせて、複数ドメインを扱う場合はドメインごとに assetlinks.json が必要です。1つのドメインに置いたファイルは、他のドメインをカバーしません。

さらにバージョンによる差もあります。Android 12 以降は検証がドメイン単位なので、あるドメインが失敗しても他には影響しません。しかし Android 11 以前は、宣言したドメインの1つでも検証に失敗すると、全ドメインが巻き込まれて失敗します。古い端末もサポートするなら、宣言したすべてのドメインで有効な assetlinks.json が用意できているかを、公開前に必ず確認しましょう。

原因4:autoVerifyの付け忘れ

基本的ですが、android:autoVerify="true" が intent-filter に付いていなければ自動検証は走りません。複数の intent-filter がある場合は、検証させたいものそれぞれに個別に付けるのが確実です。

端末側で検証ステータスを確認する

設定が正しいはずなのに開かない、というときは、端末が実際にどう判定しているかを見ます。アプリをインストールした状態で、次のコマンドを実行します。

adb shell pm get-app-links --user cur com.example.sampleapp

出力に各ドメインの状態が表示されます。読み方はこうです。

  • verified:検証済み。リンクはアプリで直接開く。
  • selected:ユーザーが手動でこのアプリを選んだ状態(自動検証ではない)。
  • none:まだ検証が走っていない、または状態がリセットされている。

設定を直したあと、再インストールせずに検証をやり直したいときは、状態をリセットしてから再検証をかけます。

# 現在の検証状態をリセット
adb shell pm set-app-links --package com.example.sampleapp 0 all
 
# 再検証を実行
adb shell pm verify-app-links --re-verify com.example.sampleapp

反映タイミングの罠

「サーバーのファイルを直したのに、まだアプリで開かない」――これはバグではなく反映待ちのことが多いです。OSバージョンで挙動が違います。

  • Android 15(API 35)以降:システムがバックグラウンドで定期的に再検証します。ただしキャッシュと再検証スケジュールの都合で、変更が全端末に行き渡るまで最大7日かかることがあります。
  • Android 14(API 34)以前:定期的な再検証は行われません。基本はインストール/更新時にだけ反映されるので、テストではアンインストール→再インストールで強制的に取り直します。

なお、再インストールしてもサーバー側やCDNのキャッシュで古いファイルが配られ続けることがあります。その場合は時間を置くと自然に解消することが多いです。

まとめ:切り分けチェックリスト

  • curl -sIL で、リダイレクトなしの 200 & Content-Type: application/json を確認した
  • SHA-256 は Play Console の署名鍵のもので、大文字で記載した
  • package_name が実際のアプリと一致している
  • 複数ドメインは、各ドメインに個別の assetlinks.json を置いた
  • intent-filter に autoVerify="true" を付けた
  • adb shell pm get-app-linksverified を確認した
  • 反映待ち(Android 15は最大7日 / 14以下は再インストール)を考慮した

App Links の不具合は「無言で失敗する」のがつらいところですが、逆に言えば確認できるポイントは決まっているので、上から順に潰していけば必ず原因にたどり着けます。中でも Play App Signing の SHA-256 は本当に事故が多いので、まずそこから疑うのがおすすめです。URLハンドリング系の話は他にもあるので、また少しずつ書いていければと思います。

コメント

タイトルとURLをコピーしました