注意点・iOS の制約
PWA は楽しいが「思ったように動かない」場面が多い。特に iOS Safari の独自仕様、SW の更新地獄、ストレージ枯渇など。 本番投入前のチェック集。
iOS Safari の主要な制約
- Web Push: 16.4+ で対応、ただしホーム画面追加が必須
- Background Sync / Periodic Sync / Background Fetch: 未対応
- Web Bluetooth / NFC: 未対応
- ストレージ容量制限が厳しい(後述)
- SW のスコープ・キャッシュが独立(Safari と PWA で別)
- 3rd-party Cookie / Storageに厳しい(ITP)
- カメラ・マイクは getUserMedia OK、ただし PWA で初回許可ダイアログが微妙
- WebGL2 OK、WebGPU は新しめ iOS のみ
- Web Share OK、Share Target は限定的
iOS のストレージ制限
iOS はストレージ管理が厳しい:
- 未使用のサイトのSW・Cache が数週間で削除される
- 容量上限が小さい(数百 MB 程度)
- 容量がいっぱいになると古いオリジンから削除
navigator.storage.persist()で永続化を要求できるが、iOS は許可が稀
容量チェック
const { quota, usage } = await navigator.storage.estimate()
console.log(`${(usage / 1024 / 1024).toFixed(1)} MB / ${(quota / 1024 / 1024).toFixed(1)} MB`)
if (usage / quota > 0.8) {
// 80% 超えたらキャッシュを刈る
await trimCaches()
}
SW の更新地獄
典型症状
- 「ユーザーから古い画面が見えるって言われる」 — SW が古いキャッシュを返している
- 「タブを閉じても更新されない」 — SW の lifecycle
- 「
force-reloadでも直らない」 — SW + Cache が独立
対策
- HTML を Network First。古い HTML を絶対残さない
- JS / CSS はファイル名にハッシュを入れて Cache First
- キャッシュキーをデプロイごとに変える(バージョン文字列)
- 更新通知 UI を出して「リロード」ボタン
- 緊急時用の「キャッシュ全削除 + 再起動」ボタンを仕込んでおく
- SW 自体を
updateViaCache: "none"でブラウザキャッシュしない
HTTPS 必須の例外
localhost/127.0.0.1/file://は除外- 本番では HTTPS 必須。Let's Encrypt or CDN(Cloudflare 等)
Lighthouse PWA 監査
Chrome DevTools → Lighthouse → "Progressive Web App" カテゴリ。基準を満たすかチェック。
- HTTPS
- HTTP → HTTPS リダイレクト
- レスポンスサイズ
- SW がインストール可能
- Manifest が valid
- アイコン (192, 512)
- theme-color meta
- viewport meta
- maskable アイコン
パフォーマンス
- 初回ロードをCritical CSS インラインで速く
- Service Worker は軽量に保つ(数 KB)
- キャッシュは適切なサイズ管理(古いの削除)
- 大きな静的アセットはCDN + 長期キャッシュ
- 画像は
<picture>+ WebP/AVIF + lazy loading
セキュリティ
- SW は同一オリジンのリクエストすべてを見られる。重要
- SW のスクリプトを外部 CDN に置かない(侵害されると全部抜かれる)
- CSP(Content-Security-Policy)で SW を
selfに - キャッシュには秘匿情報を入れない(クッキー扱いで他端末に渡る可能性)
- Push 通知のペイロード暗号化はVAPID + 自動暗号化で対応されるが、機密情報はサーバ ID だけ送ってクライアントで取りに行く方が安全
App Store / Play Store
「Web だけで配布」だと検索性が落ちる。ストアにも出すのが定石:
- Android: PWA Builder で TWA → Google Play
- iOS: Capacitor / PWA Builder iOS Wrapper(基本ネイティブを薄くラップ)
- App Store の独自規約に注意(決済・スクリーンショット等)
テスト
- シークレットモードで初回挙動を試す
- 機内モードでオフライン UX
- SW を Unregisterして新規ユーザー再現
- iOS は実機が必須(シミュレータと挙動が違うことが多い)
- Lighthouse / WebPageTest で計測
よくある罠
- SW が登録されない — HTTPS / scope / ファイル URL 確認
- ホーム画面追加で起動しても普通の Safari — manifest の
display: standalone - ロゴが切れる — maskable アイコンの安全領域に収める
- iOS スプラッシュが真っ白 —
apple-touch-startup-image必要 - キャッシュが消えない — キャッシュ名が同じ。バージョン付ける
- Push が来ない — VAPID / endpoint / iOS の場合 PWA インストール状態
- Background Sync が動かない — Safari は未対応。フォールバック必要
- 古いアセットを参照する HTML — HTML を Cache First にしてしまった事故
判断ガイド
| 要件 | PWA でいい? |
|---|---|
| シンプルなオフライン読み | ○ |
| シンプルな通知 | ○(iOS は ホーム画面追加必須) |
| 本格的なオフライン書き込み + 同期 | △(Safari は工夫要) |
| 定期実行(バックグラウンド) | △(Chromium のみ) |
| Bluetooth / 高度なセンサー | ×(ネイティブ) |
| App Store 検索流入が必須 | △(ストア配布で対応) |
| 常駐バックグラウンド処理 | × |
参考リンク
web.dev — Learn PWA / MDN PWA / PWA Builder / vite-plugin-pwa