WebプレゼンスとSEO
目的
両ストアはアプリを提出する前にURL(プライバシーポリシー、サポート、アカウント削除、アプリリンク用の.well-knownファイル)を要求します。このスキルは、まずそれらのフォームを満たす小さく高速な静的サイトを構築し、次にトラッカーやダークパターン、アプリが裏付けできない数字を使わずに、検索エンジン、リンクプレビュー、AIアシスタントから見つけられるようにします。
これはストア登録とプライバシーフォーム(listing-and-aso、privacy-and-declarations)の前に行ってください。ライブURLが必要です。
前提条件
- あなたが管理するドメインとDNSアクセス(例ではCloudflareを使用していますが、ヘッダーを設定しファイルをリダイレクトなしで配信できるホストであれば何でも構いません)。
identity-and-signingからのアプリの最終的な識別子と署名データ(Team ID、バンドルID、パッケージ名、Play アプリアップグレード用 SHA-256)。privacy-and-declarations(コード/SDKスキャンから導出)からのプライバシーポリシーテキストと、アプリ内に実際に存在する削除フローの内容。subscriptions-and-paymentsからのストア読み戻しの価格(記憶から入力しないこと)。- Node.js 18+(scripts/内のスクリプト用)。
1. ストアが必要とするページ
すべてのストアフィールドとURLの対応チェックリスト:references/store-url-checklist.md
| ページ | パス(推奨) | ルール |
|---|---|---|
| プライバシーポリシー | /privacy | 公開、ログイン不要、ジオブロッキングなし、HTML(PDF不可)、アプリ名を記載、ストアのプライバシー回答と一致 |
| 利用規約 | /terms | サブスクリプションを販売する場合に必要(Apple標準EULAまたは独自のもの、アプリとストア登録にリンク) |
| サポート/問い合わせ | /support | 実際の連絡手段(メールまたはフォーム)、FAQのみではない |
| アカウント削除(Web) | /delete-account | Google Playで必須。ストア登録と同じアプリ名を記載、ページからユーザーが削除をリクエスト可能、削除されるもの・保持されるものとその期間、ストアのサブスクリプションは別途キャンセルする必要があることを明記 |
- Appleは削除をアプリ内で開始することを要求します(ガイドライン5.1.1(v))。Webページはこれを代替しません。Playはアプリ内パスとWeb URLの両方を要求します。ドキュメント:Appleアカウント削除、Playアカウント削除、PlayプライバシーポリシーURLルール
- サインアウト状態でも安定。 クッキーウォール、ログイン、テキスト読み取りにJavaScript不要。提出後にパスを変更しないこと。変更する場合は、古いパスから301リダイレクトを維持すること。
- 言語別。 言語ごとに1つのURL(
/privacy、/de/privacy)にhreflangを設定。App Store ConnectではロケールごとにプライバシーURLを設定可能。ページが翻訳されていない場合は、404ではなくデフォルト言語のページにリンクすること。 - 法律ページには「最終更新日」を表示し、古いバージョンはバージョン管理に保持すること。
2. アプリリンクのホスティング
ファイル自体のテンプレートはidentity-and-signing/templates/にあります。このセクションではそれらの配信について説明します。
https://<domain>/.well-known/apple-app-site-associationおよび/.well-known/assetlinks.json:HTTP 200、Content-Type: application/json、リダイレクトなし(http→https、apex→www、末尾スラッシュ追加のいずれも不可)、認証なし、ボットチャレンジページなし。- apexと
wwwのどちらを使うかは一度決める。エンタイトルメント/インテントフィルターにリストされているすべてのホストでファイルを配信し、もう一方のホストはページのみリダイレクト(/.well-known/はリダイレクトしない)。 - 静的ホストでは、拡張子なしのAASAを
application/octet-streamとして配信することがよくあります。Workerを介して/.well-known/*をルーティングするか(テンプレートを参照)、ヘッダーを明示的に設定してからscripts/check-site.mjsで確認してください。 - ドメインを主張するアプリのビルドの前に
.well-knownファイルをデプロイしてください。AppleはAASAをCDN経由で取得するため、変更が反映されるまで時間がかかることがあります。
招待/紹介ページ(/i/<code>)
- Workerはコードをバックエンドが発行する正確なアルファベットと長さに対して検証し、テンプレートを埋めます。URLからHTMLに到達するものは他にありません。
- アプリがインストールされている場合、ユニバーサルリンク/アプリリンクが直接開きます(パスがAASAの
componentsとAndroidのインテントフィルターにある必要があります)。インストールされていない場合、ページには両方のストアボタンと手動フォールバックとして「コードをコピー」ボタンが表示されます。 - Android:Play URLに
&referrer=<urlエンコードされたkey=value>としてコードを追加し、PlayインストールリファラーAPIを使用してアプリで読み取ります。iOSにはインストールリファラーはありません。ユーザーにコードを貼り付けてもらうか、明示的なタップ後にのみクリップボードから事前入力します。 - ヘッダー:
X-Robots-Tag: noindex、Cache-Control: private, no-store、Referrer-Policy: no-referrer。/i/をサイトマップから除外すること。robots.txtでDisallowしないこと。ページを取得できないクローラーはnoindexを認識しません。
3. ランディングページ
- ストアのスクリーンショットと同じビジュアル言語(色、フォント、同じ実際のUIカード)。サイトから訪れた人がストアページを認識できるようにすること。
- ストアバッジは公式アートワークのみを使用し、未変更で、必要な間隔と文言を守り、ストア登録に直接リンクすること。ルール:Appleマーケティングガイドライン、Google Playバッジ
- ローンチ前:「Coming soon to the App Store and Google Play」の1行のみ。無効なバッジ3つは表示しない。ローンチ時(リリース公開後):実際のバッジリンクに切り替え、Smart App Bannerメタタグを追加(ドキュメント)、
llms.txtとJSON-LDを更新。これを単一の設定フラグとして管理し、1回の変更で済ませること。切り替えには承認が必要。 - 正直な価格。 価格はストア読み戻しから生成された1つのファイルから取得します。ページにはプレースホルダー(
%%PRICE_MONTHLY%%)が含まれています。Workerが訪問者の国(request.cf.country)に基づいてこれらを埋め、テーブル外の国には「ストアがお客様の通貨で正確な価格を表示します」と表示します。ページの言語が通貨を決定することはありません。取り消し線の「以前の」価格、偽のカウントダウン、事前チェックされたものは一切なし。 - 高速でアクセシブル。 テキストはHTMLで提供(JSでレンダリングしない)、CSSは1ファイル、JSは最小限またはなし、セルフホストフォントで
font-display: swap、ヒーロー画像はサイズ指定とfetchpriority="high"。目標値:LCP < 2.5秒、CLS < 0.1、INP < 200ミリ秒(Web Vitals)。WCAG 2.2に従ったコントラストとフォーカス状態、prefers-reduced-motionを尊重。 - アナリティクス。 なし、またはサーバーサイド/クッキーレスカウント(エッジログ、プライバシー重視のアナリティクスで識別子なし)を優先。クッキーを設定せず、サードパーティスクリプトを読み込まない場合、クッキーバナーは不要。「念のため」表示しないこと。トラッカーを追加する場合は、同意バナー、プライバシーポリシー、ストアのプライバシー回答をすべて一緒に変更する必要があります。
4. ソーシャルシェア画像
- 言語ごとに1つのOpen Graph画像:1200×630のPNG/JPG、約1MB以下、サムネイルサイズでも読める大きさのテキスト。
og:imageとtwitter:imageには絶対https://URLを使用し、og:image:width/height/alt、og:title、og:description、og:url、twitter:card=summary_large_imageを指定。仕様:ogp.me、Meta共有ガイド - メッセージングアプリはプレビューを積極的にキャッシュします。画像を変更した後は、新しいファイル名(または
?v=2)を付け、Sharing Debuggerで再スクレイピング(「Scrape Again」)してください。WhatsAppなどのメッセンジャーは公開リフレッシュツールがない独自のキャッシュを保持するため、新しい画像URLが信頼できる解決策です。新しいチャットでリンクを送信してテストしてください。
5. 技術的SEO
- タイトル(約50~60文字、ブランド名+説明)とメタディスクリプション(約150~160文字)はページと言語ごとにユニークに。タイトルリンク、スニペット
- hreflang:各言語バージョンはすべてのバージョン(自身を含む)と
x-defaultをリストし、ペアは相互でなければなりません。ローカライズ版 - Canonical:自己参照、絶対URL、最終的な(リダイレクトしない)URLを使用。正規化
- サイトマップ:200、インデックス可能、正規URLのみを含み、実際の
<lastmod>(例:gitからのコンテンツ変更日、ビルド時間ではない)を設定。テンプレート、ドキュメント - robots.txt:テンプレート、紹介。robots.txtはクロールを制御し、インデックスは制御しません。結果に表示したくないページには
noindexを使用します。 - 構造化データ(JSON-LD):Organization、WebSite、MobileApplication。FAQPageはページ上に表示されているFAQにのみ使用。例とルール:references/structured-data.md
- 非プロダクションホスト(プレビュー、
*.workers.dev)はX-Robots-Tag: noindexを送信します。
6. 発見性向上
- Google Search Console:Domainプロパティを追加し、apexに
google-site-verificationTXTレコードを追加、確認、サイトマップを送信、ホームページのインデックスをリクエスト。所有権確認 - Bing Webmaster Tools:Search Consoleからインポートするか、Bingから提供されたDNSレコード(CNAME)で確認し、サイトマップを送信。追加と確認
- IndexNow(Bing、Yandex、Seznam、Naverなど、Googleは非対応):
/<key>.txtをホストし、キーを含めてデプロイし、node scripts/indexnow-ping.mjs(ドライラン)と--sendを実行。スクリプトは最初にキーファイルが公開されているか確認します。プロトコル - AIクローラーとllms.txt:ボットごとに検索/回答ボットとトレーニングボットの許可/拒否を決定(所有者の判断、承認が必要)。ボット一覧、目的、公式リンク:references/ai-crawlers.md。製品概要と主要URLを含む短いllms.txtを追加します。価格は含めないでください。
DNSレコードは共有インフラです。新しいレコードを追加し、既存のTXT/SPF/MXレコードは編集または削除しないでください。DNS変更には承認が必要です。
7. デプロイ(Cloudflare Workerの例)
テンプレート:templates/worker.example.js。適切なタイプとリダイレクトなしで.well-knownファイルを配信し、国別価格プレースホルダーを埋め、/i/<code>をレンダリングし、セキュリティヘッダーと非プロダクションホストでのnoindexを追加します。
2つのストレージオプション:
- Workers Static Assets(最もシンプル):
wrangler.jsoncで"assets": { "directory": "./dist", "binding": "ASSETS", "run_worker_first": true }。Workerがenv.ASSETS.fetch()を呼び出します。ドキュメント - KV(コンテンツアドレス方式):ビルドでファイルをハッシュと
manifest.js(パス→ハッシュ、タイプ)の下に書き込み、wrangler kv bulk putでWorkerをデプロイする前にアップロードします。新しいマニフェストが存在しないファイルを指さないようにするためです。KVコマンド
手順:
node build.mjs→dist/(不足している翻訳キーや価格以外の未入力プレースホルダーがあると失敗)。- ユニットテスト(
node --test):スタブアセットに対してWorkerのfetchを実行。カバー範囲:AASA/assetlinks 200 +application/json+ リダイレクトなし。www/httpのページのみリダイレクト。/i/<bad>→ 404、/i/<good>→ noindex + no-store。すべての価格地域がすべてのプレースホルダーを埋める(%%が残らない)。不正なパーセントエンコーディング → 500ではなく404。 - プレビュー:
npx wrangler deploy --dry-run、またはプレビューホストにデプロイしてnode scripts/check-site.mjs https://<preview-host>を実行。 - 承認 — プロダクションデプロイ(
npx wrangler deploy)。wrangler.jsoncでaccount_id、name、ルートを固定し、共有アカウントで別のプロジェクトをターゲットにできないようにする。 - デプロイ後:
node scripts/check-site.mjs https://example.com --invite <サンプルコード>、次にIndexNowピング。
人間の承認が必要な決定ポイント
- プロダクションデプロイとDNS変更。
- 法律ページ(プライバシー、利用規約、削除)の文言。所有者が変更に署名します。
- AIトレーニングクローラーの許可/拒否。
- 「近日公開」→「ダウンロード」の切り替えとサイトに表示される価格。
- アナリティクス、ウェイトリストフォーム、または個人データを収集するものの追加。
検証
node scripts/check-site.mjs https://example.com --paths /privacy,/terms,/support,/delete-accountが0で終了:必要なページがサインアウト状態で200、AASA/assetlinksがリダイレクトなしのJSON、OGタグに到達可能な絶対画像、相互のhreflang、サイトマップURLがすべて200。- ストアURLチェックリストのすべてのURLがスマートフォンのプライベートウィンドウで開くこと。
- リッチリザルトテストとスキーママークアップバリデーターでエラーなし。PageSpeed InsightsモバイルLCP < 2.5秒。
- Search Console:サイトマップ「Success」。Bing:IndexNow送信が表示される。
よくある落とし穴
- ログイン、PDF、またはアプリ名を記載していない一般的な会社ポリシーにリダイレクトするプライバシーURL。
- 削除ページで「アプリ内で削除してください」とだけ書かれている(Playはページ上にリクエストパスを要求)か、ストアのサブスクリプションキャンセルを忘れている。
- AASAがapex→wwwリダイレクト、
text/html、またはボットチャレンジの背後で配信されている。 - robots.txtで
Disallow: /i/とnoindexヘッダーを同時に使用し、クローラーがnoindexを認識できない。 - 価格が言語ごとにハードコードされている(スイスの訪問者に対してドイツ語ページでユーロ表示)、またはストアの価格変更後に古い数字が残っている。
- 新しいアプリに
aggregateRatingをでっち上げる、またはoffersの価格が1か国だけにしか当てはまらない。 - OG画像のURLが相対パス、またはHTTPであり、HTTPSでない。
関連スキル
identity-and-signing— このサイトで使用されるバンドルID、パッケージ名、チームID、SHA-256フィンガープリント。privacy-and-declarations— プライバシーポリシーの本文を生成し、データ削除に関する質問に答えます。subscriptions-and-payments— 価格プレースホルダーに入力するストア価格を読み戻します。listing-and-aso— このサイトとストア登録ページに表示されるストアテキストを生成します。