本文へスキップ
すべての記事

2026年のAndroid App Links: https://のリンクがまだブラウザで開いてしまう理由

Digital Asset Linksの検証、assetlinks.jsonに潜む落とし穴、そしてAndroidがなぜリンクをアプリに渡さないのかを教えてくれるadbコマンド。

MFKAPPS 1 分で読めます

myapp://item/42 のようなカスタムURIスキームは設定が簡単な反面、別の形で壊れやすい。同じスキームはどのアプリでも登録できるため、OSはあなたのアプリがそれを開く正当な相手だと保証できない。Android App Link——ブラウザのタブではなくアプリを直接開く本物の https://yourdomain.com/... URL——はこれを解決するが、それはOSがあなたのアプリが本当にそのドメインを所有していると検証できた場合に限る。ほとんどの場合、こうしたリンクは静かにChromeにフォールバックし、マニフェストは完全に正しく見える。欠けているピースはほぼ常にintent filterではなく、Digital Asset Linksの検証だ。

一致しなければならない二つのもの

App Linkは、独立した二つの要素が一致したときにだけ自動検証される。アプリのintent filterと、ドメイン自身が配信するJSONファイルだ。どちらも相手の存在を知らないというのがまさに肝心な点であり、それが無関係なアプリがあなたのドメインを名乗ることを防いでいる。

intent filterはマニフェストに書く。

<activity android:name=".MainActivity" 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="mfkapps.com"
              android:pathPrefix="/apps" />
    </intent-filter>
</activity>

android:autoVerify="true" は、単にURLパターンを照合するだけでなく、インストール時にAndroidが実際に所有権をチェックすることを引き起こすものだ。これを省くとフィルタはリンクにマッチし続けるが、それはユーザーが曖昧さ回避シートから明示的に一度アプリを選んだ後だけであり、「常に自動的に開く」レベルに昇格することは決してない。

ドメイン側は、固定された交渉の余地のないパスに置かれる静的ファイルだ。

https://mfkapps.com/.well-known/assetlinks.json
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.mfk.granyn",
    "sha256_cert_fingerprints": [
      "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A3:88:1E:22:0B:99:19:33:4B:C0:5B:0B:64:0C:1D"
    ]
  }
}]

package_namesha256_cert_fingerprints の両方が、インストールされているまさにそのビルドと一致しなければならない。ほとんど壊れた設定が実際に壊れているのは、このフィンガープリントの詳細部分だ。

すべてを静かに壊すフィンガープリントの不一致

sha256_cert_fingerprints には、ユーザーがインストールしたAPKの署名証明書のSHA-256が必要だ——そしてアプリがPlay App Signing経由で配布されている場合、それはローカルで署名するアップロードキーではなく、Googleの署名鍵になる。assetlinks.json に間違ったフィンガープリントを入れると、検証はユーザーには一切見えないエラーのまま失敗する。リンクは永遠にブラウザで開き続け、Logcatにはこのファイルを指し示すものが何もない。

正しいフィンガープリントは、ローカルのキーストアからではなくPlay Consoleから取得する。

Play Console → Setup → App integrity → App signing key certificate → SHA-256

開発中のデバッグビルド用には、デバッグキーストアのフィンガープリントを使い、デバッグとリリースを同じ assetlinks.json で検証させたい場合は両方を配列に入れる。

keytool -list -v -keystore ~/.android/debug.keystore \
  -alias androiddebugkey -storepass android -keypass android

sha256_cert_fingerprints は配列を受け付けるので、二つのフィンガープリントを並べて置くことができる——検証は、インストールされたアプリの署名にいずれかのエントリが一致するかをチェックする。

実際にうまくいったかを確認する

Androidはインストール時に検証チェックを実行し、その結果をキャッシュする。だから本当の状態を見る一番早い方法は、挙動から推測することではなく、OSに直接尋ねることだ。

adb shell pm get-app-links com.mfk.granyn

正しく動作している設定はドメインを verified として報告する。

com.mfk.granyn:
  ID: d3a1f0b2-...
  Signatures: [14:6D:E9:...]
  Domain verification state:
    mfkapps.com: verified

legacy_failurenone と表示される場合は、フィンガープリントかJSONが間違っている——assetlinks.json を修正し、再インストールせずに再チェックを強制する。

adb shell pm verify-app-links --re-verify com.mfk.granyn
adb shell pm get-app-links com.mfk.granyn

検証が通った後で実際のタップ挙動をテストするには、ブラウザや他のアプリがするのと同じ方法でintentを発火させる。

adb shell am start -a android.intent.action.VIEW \
  -d "https://mfkapps.com/apps/granyn"

選択シートなしでアプリが開けば、検証はうまくいっている。曖昧さ回避のダイアログが出るなら、検証は失敗しており、OSはそれを通常の未検証リンクマッチとして扱っている。

公開後に静かに壊す三つのこと

JSONは正しいコンテンツタイプで、リダイレクトなしに配信される必要がある。 /.well-known/assetlinks.json を正規URLへ301リダイレクトするCDNやリバースプロキシ、あるいはそれを text/html として配信するものは、ブラウザでは問題なく表示されても検証を失敗させる。両方を直接curlで確認する。

curl -sI https://mfkapps.com/.well-known/assetlinks.json | grep -i content-type

アプリを再署名するとフィンガープリントが変わる。 署名鍵のローテーション、ローカルキーストアからPlay App Signingへの切り替え、新しいビルドパイプラインの導入は、assetlinks.json を新しいフィンガープリントで更新するまで、それまで検証済みだったすべてのリンクを静かに壊す。これはリリースチェックリストに一行加える価値がある。失敗モード——リンクが静かにブラウザフォールバックに落ちること——はクラッシュもエラーレポートも生まないからだ。

一つのドメインを複数のアプリが共有する場合、複数のステートメントが必要になる。 同じチームの複数のアプリが同じホスト配下のパスを名乗る場合、assetlinks.json はJSONの配列になる——アプリごとに一つのオブジェクトで、それぞれが独自の package_name とフィンガープリントリストを持つ。一つのアプリのステートメントしか含まないファイルは、そのドメイン上の他のすべてのアプリの検証を静かに失敗させる。

これが快適なタップ以上にもたらすもの

ドメインが検証されると、同じintent filterによって、OSがURLを引き渡すあらゆる場面——通知アクション、QRコード、他のアプリから共有されたリンク、Smart Lockの認証情報フロー——でアプリがそのドメインのデフォルトハンドラとして振る舞えるようになる。そのどれも別途の配線を必要としない。同じ autoVerify フィルタと同じ assetlinks.json が、OSがその裏付けを本当に信頼したときに、より多くのことをこなしてくれるだけだ。

これらすべてを終えてもアプリへのリンクがまだブラウザで開くなら、マニフェストに再び手を入れる前に pm get-app-links を確認してほしい——実際のところ、壊れている側がintent filterであることはほとんどない。