PWAは同じドメインから複数インストールできる?id・scope・start_urlの関係



当サイト、オリジナルWordPressプラグイン「OJapp PWA Marketing」を使用しています。

各記事は専用アイコンでホーム画面に追加できます。ぜひお試しください。

PWAを作っていると、こんな疑問が出てきます。

同じドメインから、PWAを2個、3個とインストールすることはできるのか?

例えば、サイト全体のアプリとは別に、ツールだけをホーム画面へ置きたい。さらに特定の商品ページや記事も、それぞれ別のアイコンとして置きたい。

Webには1つのドメインの中に大量のURLがあります。それなのにPWAだけ「1ドメイン=1アプリ」と決めてしまうと、少し窮屈です。

実際には、PWAの単位はドメインだけで決まるわけではありません。Web App Manifestのid、start_url、scope、そしてブラウザやOS側の実装が関係します。

さらに設計次第では、サイト単位、グループ単位、ページ単位だけでなく、ユーザーが選んだURLの状態をホーム画面の入口にすることもできます。

まずid・start_url・scopeは役割が違う

この3つは一緒に出てくることが多いので混乱しやすいのですが、それぞれ役割が違います。

  • id:そのPWAを識別するためのID
  • start_url:ホーム画面から起動した時に最初に開くURL
  • scope:そのPWAとして扱うナビゲーション範囲

かなり大ざっぱに言えば、「誰なのか」「どこから始めるのか」「どこまでをアプリとして扱うのか」です。

似ているように見えますが、同じ設定ではありません。

1S1Aならサイト全体を1つのPWAとして扱う

一般的なPWAは、サイト全体を1つのアプリとして設計することが多いです。

例えば次のような構成です。

{
  "id": "/",
  "start_url": "/",
  "scope": "/",
  "display": "standalone"
}

サイト全体を1つのサービスとして使うなら、とても分かりやすい設計です。

example.com
├─ /
├─ /news/
├─ /products/
├─ /account/
└─ /tools/

        ↓

     1つのPWA

私はこの考え方を1S1A(One Site. One App.)と呼んでいます。

1S1Aと1P1Aの違いでも紹介していますが、1S1Aと1P1Aはどちらが優れているという話ではありません。「何を1つのAppとして扱うか」の違いです。

でも同じドメインに複数の機能がある

問題は、1つのサイトの中に独立した機能が増えてきた時です。

example.com/
example.com/shop/
example.com/tools/
example.com/docs/

サイト全体のPWAは欲しい。

でも/tools/は頻繁に使うので、ツール専用アプリとしてもホーム画面へ置きたい。

/docs/も独立したドキュメントアプリのように使いたい。

こうなると、「同じドメインだから全部1つ」という設計だけでは足りなくなります。

ディレクトリ単位なら1G1Aという考え方もできる

そこで使えるのが、私が1G1A(One Group. One App.)と呼んでいる設計です。

例えば/tools/を1つのグループとして扱うなら、次のように明示できます。

{
  "id": "/tools/",
  "start_url": "/tools/",
  "scope": "/tools/",
  "display": "standalone"
}

/docs/なら、

{
  "id": "/docs/",
  "start_url": "/docs/",
  "scope": "/docs/",
  "display": "standalone"
}

というように、それぞれ別の入口、識別情報、ナビゲーション範囲を持たせる設計ができます。

example.com/
   └─ Site App

example.com/tools/
   └─ Tools App

example.com/docs/
   └─ Docs App

同じオリジンの中でも、「サイト」「ツール群」「ドキュメント群」のようにAppの単位を分けて考えられるわけです。

アッピン
1ドメイン=絶対1アプリ、って考えなくてもいいんだね👻 URLの構造そのものをAppの単位として使える。

1S1Aの仕組みを使って1G1Aにもできる

面白いのは、1G1Aのためにまったく別のPWA技術が必要なわけではないことです。

OJappでも、1G1Aは1S1A側の仕組みを利用し、id、start_url、scopeをディレクトリへ明示することで作れます。

<meta name='ojapp:id' content='/tools/'>
<meta name='ojapp:start-url' content='/tools/'>
<meta name='ojapp:scope' content='/tools/'>

<script src='https://ojapp.app/js/ojapp_1s1a.js'></script>

つまり、1S1A用の仕組みを使うからといって、必ずルート/を1つのAppにしなければならないわけではありません。

Manifestの設計を変えれば、ディレクトリ単位の入口にもできます。

さらに1P1Aを組み合わせることもできる

ここからさらに細かくできます。

例えばサイト全体やツール群とは別に、特定のページだけをホーム画面へ置きたい場合です。

example.com/
   └─ Site App(1S1A)

example.com/tools/
   └─ Tools App(1G1A)

example.com/tools/timer/
   └─ Timer(1P1A)

example.com/products/coffee/
   └─ Coffee(1P1A)

1P1A(One Page. One App.)では、サイト全体ではなくページを1つのApp単位として考えます。

タイマーならタイマーの名前とアイコン、商品なら商品画像と商品名というように、ページそのものをホーム画面の入口として扱えます。

詳しい考え方は1P1Aとは?1ページを1アプリとして扱うPWA設計でも整理しています。

つまり、1S1A・1G1A・1P1Aは必ずどれか1つだけを選ぶものではありません。

1つのWebサイトの中で、用途に合わせて複合させることもできます。

さらにユーザーが最後の状態を決めるUDA

ここからもう一段面白い使い方があります。

例えば、次のタイマーを1P1Aとしてホーム画面へ追加できるとします。

https://example.com/timer/

これだけなら「タイマーページを1つのAppとして扱う」という1P1Aです。

でもWebでは、URLのクエリに現在の設定状態を持たせることができます。

/timer/?time=3&mode=down
/timer/?time=5&mode=down
/timer/?time=20&mode=up

同じタイマーページでも、それぞれ意味が違います。

3分カウントダウン、5分カウントダウン、20分カウントアップ。

ここで、その設定済みURLを起動状態としてホーム画面へ追加できれば、同じページからさらに複数の入口を作れます。

Timer Page
├─ 3m ↓
├─ 5m ↓
└─ 20m ↑

私はこの考え方をUDA(User Defined App)と呼んでいます。

UDAは1P1Aの「次の階層」というだけではない

ここは少し大事です。

URLだけを見ると、

Site
↓
Group
↓
Page
↓
State

と細かくなっていくので、UDAが1P1Aのさらに下の階層のようにも見えます。

ただ、UDAの本質は単純な階層分けではありません。

最後のAppの状態を、サービス提供側ではなくユーザー自身が決める。

そこがポイントです。

開発者はタイマーというWeb機能を提供する。

ユーザーは「5分・カウントダウン」のように自分が使いたい状態を選ぶ。

その結果できたURLを、そのまま自分専用のホーム画面入口として追加する。

Developer
↓
タイマー機能を提供
↓
User
↓
5分・カウントダウンを選択
↓
/timer/?time=5&mode=down
↓
ホーム画面へ追加

つまりUDAは、1P1Aの次の階層というより、ユーザーがURLの状態を選ぶことで自分のApp入口を定義する考え方です。

1P1Aのようなページ単位の設計と組み合わせながら、最後の設定をユーザーに決めてもらう形でホーム画面へ追加してもらうこともできます。

UDAについてはPWA UDA(User Defined App)とは?ユーザーが自分のAppを定義する新しい設計思想でも詳しく紹介しています。

同じページから複数のApp入口が生まれる

この考え方まで入れると、「同じドメインから複数インストール」という話がかなり広がります。

example.com/
   └─ Site App

example.com/tools/
   └─ Tools App

example.com/tools/timer/
   └─ Timer App

example.com/tools/timer/?time=3&mode=down
   └─ 3m ↓ Timer

example.com/tools/timer/?time=20&mode=up
   └─ 20m ↑ Timer

最初は「同じドメインから複数のPWAを作れるか?」という話だったのに、最終的には同じページの同じ機能から、ユーザーの設定によって複数のホーム画面入口を作るところまで考えられます。

ただし、クエリを含むPWAの識別や複数インストールの扱いはブラウザやプラットフォームによって差が出る可能性があります。

OJappではクエリを利用した構成を用意していますが、特に複数インストールを前提にする場合は対象端末で実際に確認するのが安全です。

idは「起動するURL」ではない

複数PWAを考える時に特に混乱しやすいのがidです。

idはホーム画面から開くURLを指定するものではありません。

起動先はstart_urlです。

idは、そのPWAの識別情報として使われます。

{
  "id": "/tools/",
  "start_url": "/tools/dashboard/"
}

このように、識別情報と起動先を別々に設計できます。

明示的な安定したidを持たせれば、後からstart_urlを変更しても同じPWAとして識別させる設計ができます。

逆に、有効なidを省略した場合は、Manifest仕様上、実効的なstart_urlが識別情報のフォールバックとして使われます。

ただし、idだけを変えれば、あらゆるブラウザやOSで必ず複数インストールできる、と考えるのは危険です。

最終的なインストールの扱いにはブラウザやプラットフォーム側の実装も関係します。

scopeは「アプリのID」ではない

scopeも、アプリを識別するIDそのものではありません。

Manifestのscopeは、そのPWAとして扱うナビゲーション範囲を示します。

{
  "start_url": "/tools/",
  "scope": "/tools/"
}

なら、/tools/以下をそのPWAのナビゲーション範囲として扱う設計です。

ここで注意したいのが、ManifestのscopeとService Workerのscopeは別物だということです。

ManifestのscopeはPWAのナビゲーション範囲。Service Workerのscopeは、そのService Workerが制御できるページやリクエストの範囲です。

名前が同じなので非常に紛らわしいですが、役割は違います。

PWA LABではscopeが広すぎて別アプリ化できないケースもあった

このあたりは仕様だけ読んでいるより、実際に複数のPWAを同じドメインへ置いた時の方が分かりやすいです。

PWA LABでは、同じドメイン配下にある別々のWeb機能をAndroidでホーム画面へ追加する検証をしていました。

ところが、それぞれ別のものとして追加したかったのに、両方のManifestで広いscope: "/"を使っていた構成では、Android側で期待したように別々のアプリとして扱えないケースがありました。

そこで現在の1P1A系の構成では、Androidでページごとの入口を作りたい場合に固定された広いscopeを持たせない設計も使っています。

一方、iPhoneではscopeを明示した方がホーム画面Webアプリ内の表示を整えやすいケースも実機で確認しています。

そのためOJappの実装では、用途によってiOSとAndroidでManifestの構成を変える方法も検証してきました。

これは「Androidではscopeを書いてはいけない」という一般ルールではありません。

同一ドメインに複数の入口を作るという特殊な設計で、PWA LABが実際に使っている構成の1つです。

同じドメインなら何個でも必ずインストールできる?

ここまで読むと、「じゃあidやscopeを変えれば、同じドメインから何個でもPWAを入れられるのでは?」と思うかもしれません。

そこまでは断定できません。

PWAのインストールやホーム画面追加は、Web App Manifestだけで最終的な挙動が決まるわけではないからです。

ブラウザ、OS、ManifestのURL、id、start_url、scopeなどの組み合わせによって扱いが変わる可能性があります。

特にiPhoneの「ホーム画面に追加」とAndroid ChromeのPWAインストールは、同じ操作に見えても挙動まで完全に同じではありません。

そのため、複数インストールを前提にした設計では対象端末での実機確認が重要です。

何を1つのAppと考えるか

結局、この話で一番大事なのはManifestの項目を暗記することではないと思っています。

まず、何を1つのAppとしてホーム画面へ置きたいのかを考えます。

Site
→ 1S1A

Group
→ 1G1A

Page
→ 1P1A

そして、さらにURLが状態を持つWeb機能なら、ユーザー自身が最後の状態を決めるUDAという考え方も使えます。

Site  → 1S1A
Group → 1G1A
Page  → 1P1A

User selects a State
       ↓
      UDA

UDAだけは単純な階層ではありません。

開発者が完成したAppを全部決めるのではなく、開発者はWeb機能を用意し、最後にユーザーが自分の使い方をURLとして定義する。

そのURLをホーム画面へ置けば、ユーザー自身が作った入口になります。

1ドメインの中に複数のApp入口を作れる

サイト全体を1つのAppとして使う。

その中のツール群だけを別のAppとして使う。

さらに特定のページを1つのAppとして使う。

そして対応するWeb機能なら、最後の設定をユーザーに選んでもらい、その状態を自分専用の入口としてホーム画面へ置いてもらう。

こう考えると、PWAは必ずしも「Webサイトを丸ごとアプリにする技術」だけではありません。

同じドメインの中でも、Site・Group・Pageという単位を使い分け、さらにUDAではユーザーが選んだURLの状態までホーム画面の入口にできます。

Webにもともと存在するURLという仕組みを、そのままAppの入口として使う。

私はこの設計の自由さが、PWAのかなり面白いところだと思っています。

GoogleでOJapp Tipsを優先する情報源に追加

今後、Google検索でOJapp Tipsの記事を見つけやすくできます。


OJapp FREE · 1P1A

Turn Any Web Page into a Home Screen App

Skip complex PWA setup. Add one line of code and turn your
web page into an installable home screen app.


OJapp FREE - One Page. One App.


OJapp FREE · 1P1A

Webページをホーム画面アプリに

複雑なPWA設定やmanifest.jsonの作成は不要。
1行のコードを追加するだけで、Webページをホーム画面へ追加できるアプリに変えられます。


OJapp FREEでWebページをホーム画面アプリに

最新情報をチェックしよう!
    OJapp Tips  -  PWA・ホーム画面追加の実機検証ブログ

    OJapp Tips  -  PWA・ホーム画面追加の実機検証ブログ

    OJapp Tipsは、 公式ドキュメントをなぞるだけでは分かりにくい挙動を、実際にiPhone・Android・PCで検証しながら記録しています。

    特に、manifest.json、Service Worker、Web App Manifest、WebClip、アイコンキャッシュ、ホーム画面追加の挙動など、 Webサイトを「アプリのような入口」として使うためのノウハウを中心に扱っています。

    OJapp Tipsの記事は、PWA LABでの実機検証や、OJapp・Petal・OJ-Passなど自作ツールの開発で詰まったことを元にしています。 きれいな理論だけではなく、「実際にはここでハマる」という現場寄りの知識を残すための場所です。