0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloudflare で Web アプリを公開してみよう〜SPA 公開から PWA 化まで〜

0
Last updated at Posted at 2026-10-02

この記事でわかること・前提環境

前回の記事では、Cloudflare の全体像を紹介しました。今回は実際に手を動かして、次のような Web アプリを作ります。

  • React で作った画面(SPA)を Cloudflare Workers で公開する
  • 同じ Worker の中に、簡単な API も入れる
  • PWA にして、スマホのホーム画面に追加できるようにする

すべて Cloudflare の無料プランの範囲 でできます。

ツール バージョン
Node.js v22.23.2
create-cloudflare 2.73.2
wrangler 4.147.0
Vite 8.3.2
React 19.2
vite-plugin-pwa 1.3.0

必要なのは Node.js と、無料の Cloudflare アカウントだけです。

SPA と PWA とは

  • SPA(シングルページアプリケーション)
    • 最初に1枚の HTML を読み込んで、そのあとは画面の切り替えを JavaScript で行う Web アプリです
    • ページ遷移のたびにサーバーから HTML を取り直さないので、アプリのようにサクサク動きます
    • React や Vue で作るアプリの多くがこの形です
  • PWA(プログレッシブウェブアプリ)
    • Web アプリを、スマホアプリのように使えるようにする仕組みです
    • ホーム画面にアイコンを追加でき、開くとブラウザのアドレスバーなしで全画面表示されます
    • 一度開いたファイルを端末に保存しておくので、電波が弱くても表示できます

プロジェクトを作る

Cloudflare 公式のひな形作成ツール create-cloudflare(通称 C3)を使います。

npm create cloudflare@latest -- my-pwa-app --framework=react --platform=workers

オプションを付けずに npm create cloudflare@latest だけを実行すると、対話形式で選べます。その場合は次のように選んでください。

  • What would you like to start with? → Framework Starter
  • Which development framework? → React
  • Select your deployment platform → Workers with Assets
  • Select a variant → TypeScript
  • Do you want to deploy your application? → No(あとで手動でデプロイします)
╭ Create an application with Cloudflare Step 1 of 3
│
├ What would you like to start with?
│ category Framework Starter
│
├ Which development framework do you want to use?
│ framework React
│
├ Select your deployment platform
│ platform Workers with Assets
...
🎉  SUCCESS  Application created successfully!

中では Vite の公式ツール(create-vite)が呼ばれて React のひな形が作られ、そこに Cloudflare 用の設定が追加されます。できあがる主なファイルは次のとおりです。

my-pwa-app/
├── src/              画面(React)
│   ├── App.tsx
│   └── main.tsx
├── worker/
│   └── index.ts      API(Cloudflare Workers)
├── public/           アイコンなどの静的ファイル
├── index.html
├── vite.config.ts
└── wrangler.jsonc    Cloudflare の設定

画面(src/)と API(worker/)が1つのプロジェクトに同居している のがポイントです。

Cloudflare の設定ファイル wrangler.jsonc はこうなっています(コメントは省略)。

wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-pwa-app",
  "main": "worker/index.ts",
  "compatibility_date": "2026-10-01",
  "assets": {
    "not_found_handling": "single-page-application"
  },
  "observability": {
    "enabled": true
  },
  "upload_source_maps": true
}
  • main:API を担当する Worker のコード
  • assets.not_found_handling: "single-page-application":存在しない URL(/about など)を開いたときに index.html を返す設定です。SPA では画面の切り替えを JavaScript でやるので、この設定が必要になります

API を作る

worker/index.ts に API を書きます。ひな形にも簡単な API が入っていますが、Cloudflare らしさが出るように、リクエストを処理したデータセンター を返すようにしてみます。

worker/index.ts
export default {
  fetch(request) {
    const url = new URL(request.url);

    if (url.pathname === "/api/hello") {
      return Response.json({
        message: "Hello from Cloudflare Workers!",
        // リクエストを処理したデータセンター(例: NRT = 東京)
        colo: request.cf?.colo ?? "unknown",
        time: new Date().toISOString(),
      });
    }

    return new Response(null, { status: 404 });
  },
} satisfies ExportedHandler<Env>;

request.cf には、Cloudflare がリクエストに付けてくれる情報(データセンター、国など)が入っています。colo はデータセンターを表す3文字のコードで、東京なら NRT です。

画面側(src/App.tsx)は、ボタンを押すと API を呼んで結果を表示するだけのシンプルなものにします。

src/App.tsx
import { useState } from "react";
import "./App.css";

type Hello = { message: string; colo: string; time: string };

function App() {
  const [hello, setHello] = useState<Hello | null>(null);

  const callApi = async () => {
    const res = await fetch("/api/hello");
    setHello(await res.json());
  };

  return (
    <main style={{ padding: "2rem", textAlign: "center" }}>
      <h1>My PWA App</h1>
      <button onClick={callApi}>API を呼ぶ</button>
      {hello && (
        <p>
          {hello.message}
          <br />
          データセンター: {hello.colo} / {hello.time}
        </p>
      )}
    </main>
  );
}

export default App;

画面と API が同じドメインから配信されるので、fetch("/api/hello") のように相対パスで呼べます。CORS の設定も不要です。

ローカルで動かす

cd my-pwa-app
npm run dev

表示された URL(http://localhost:5173 など)をブラウザで開き、「API を呼ぶ」ボタンを押すと、次のように表示されます。

Hello from Cloudflare Workers!
データセンター: NRT / 2026-10-02T13:35:58.041Z

表示されるデータセンターは、アクセスした場所によって変わります。ローカルで動かしていても、Cloudflare から本物のリクエスト情報を取ってきてくれるのがおもしろいところです。

npm run dev では Vite の開発サーバーの中で、本番と同じ Workers のランタイム(workerd)が動いています。そのため、API もローカルでそのまま動きます。

Cloudflare に公開する

1. Cloudflare にログインする

npx wrangler login

ブラウザが開くので、Cloudflare アカウントでログインして許可します。

2. デプロイする

npm run deploy

npm run deploy は、ビルド(npm run build)と wrangler deploy をまとめて実行するスクリプトです。ビルドすると、次のように画面用(client)と Worker 用の2つが出力されます。

vite v8.3.2 building my_pwa_app environment for production...
dist/my_pwa_app/wrangler.json        1.55 kB │ gzip: 0.76 kB
dist/my_pwa_app/index.js             0.31 kB │ gzip: 0.22 kB
✓ built in 13ms
vite v8.3.2 building client environment for production...
dist/client/index.html                   0.46 kB │ gzip:  0.29 kB
dist/client/assets/index-D64VDMd1.css    4.10 kB │ gzip:  1.47 kB
dist/client/assets/index-D2NrAUBe.js   224.58 kB │ gzip: 70.30 kB
✓ built in 322ms

デプロイが終わると、https://my-pwa-app.<あなたのサブドメイン>.workers.dev のような URL が表示されます。この URL で、世界中からアクセスできる状態 になります。

いきなり公開するのが不安な場合は、npx wrangler deploy --dry-run で「アップロードの直前まで」を試せます。

Using redirected Wrangler configuration.
 - Configuration being used: "dist/my_pwa_app/wrangler.json"
 - Original user's configuration: "wrangler.jsonc"
✨ Read 10 files from the assets directory ~/my-pwa-app/dist/client
Total Upload: 0.30 KiB / gzip: 0.22 KiB
No bindings found.
--dry-run: exiting now.

PWA にする

1. プラグインを入れる

PWA に必要なファイル(マニフェストと Service Worker)は、vite-plugin-pwa で自動生成できます。

npm i -D vite-plugin-pwa
  • マニフェスト:アプリの名前・アイコン・テーマカラーなどを書いたファイル。ホーム画面に追加するときに使われます
  • Service Worker:ブラウザの裏で動くスクリプト。ファイルを端末に保存(キャッシュ)して、オフラインでも表示できるようにします

2. アイコンを作る

ホーム画面用に、いくつかのサイズの PNG アイコンが必要です。@vite-pwa/assets-generator を使うと、SVG 1つから必要なサイズをまとめて作れます。

npm i -D @vite-pwa/assets-generator
npx pwa-assets-generator --preset minimal-2023 public/favicon.svg

public/ に次のファイルが作られます。

apple-touch-icon-180x180.png
favicon.ico
maskable-icon-512x512.png
pwa-192x192.png
pwa-512x512.png
pwa-64x64.png

3. Vite の設定に追加する

vite.config.ts
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'

import { cloudflare } from "@cloudflare/vite-plugin";

export default defineConfig({
  plugins: [
    react(),
    cloudflare(),
    VitePWA({
      registerType: 'autoUpdate',
      includeAssets: ['favicon.ico', 'apple-touch-icon-180x180.png'],
      manifest: {
        name: 'My PWA App',
        short_name: 'MyPWA',
        description: 'Cloudflare Workers で動く PWA のサンプル',
        theme_color: '#f38020',
        background_color: '#ffffff',
        display: 'standalone',
        icons: [
          { src: 'pwa-64x64.png', sizes: '64x64', type: 'image/png' },
          { src: 'pwa-192x192.png', sizes: '192x192', type: 'image/png' },
          { src: 'pwa-512x512.png', sizes: '512x512', type: 'image/png' },
          { src: 'maskable-icon-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
        ],
      },
      workbox: {
        // /api/ は Worker が返すので、SPA の index.html にフォールバックさせない
        navigateFallbackDenylist: [/^\/api\//],
      },
    }),
  ],
})

display: 'standalone' にすると、ホーム画面から開いたときにアドレスバーなしで表示されます。

index.html の <head> には、iPhone 用のアイコンとテーマカラーを追加しておきます。

index.html
<link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png" />
<meta name="theme-color" content="#f38020" />

4. ビルドして確認する

npm run build

ビルドの最後に、Service Worker が生成されたことが表示されます。

PWA v1.3.0
mode      generateSW
precache  11 entries (219.63 KiB)
files generated
  dist/client/sw.js
  dist/client/workbox-9c191d2f.js

dist/client/ に manifest.webmanifest と sw.js ができていれば成功です。もう一度 npm run deploy すると、公開中のアプリも PWA になります。

5. ホーム画面に追加する

デプロイした URL をスマホで開き、次の操作をします。

  • iPhone(Safari):共有ボタン →「ホーム画面に追加」
  • Android(Chrome):メニュー →「ホーム画面に追加」または「アプリをインストール」

ホーム画面のアイコンから開くと、アドレスバーのない、アプリのような見た目で起動します。

はまりどころ・注意点

npm create cloudflare をオプション付きで実行すると、途中で止まる

--framework=react などを指定しても、TypeScript / JavaScript の選択だけは聞かれます。自動化したいときは --variant=react-ts も付けます。

npm create cloudflare@latest -- my-pwa-app --framework=react --platform=workers --variant=react-ts --no-deploy

「Using redirected Wrangler configuration」と表示される

@cloudflare/vite-plugin を使っていると、ビルド時に dist/ の中へ wrangler 用の設定ファイルが作られ、デプロイではそちらが使われます。エラーではないので、気にしなくて大丈夫です。設定を変えるときは、元の wrangler.jsonc を編集します。

SPA のフォールバックは「ページを開いたとき」だけ

not_found_handling: "single-page-application" の設定で index.html が返るのは、ブラウザでページを開いたとき(ナビゲーション)だけです。fetch() で存在しない URL を呼んだ場合は Worker に処理が渡され、上のコードなら 404 が返ります。API の URL を間違えたときに HTML が返ってこないので、むしろ安心です。

Service Worker が API の応答を index.html で返してしまう

PWA にすると、Service Worker が「ページを開いたら、キャッシュした index.html を返す」動きをします。何も設定しないと /api/ へのアクセスまでこの対象になってしまうことがあるので、navigateFallbackDenylist: [/^\/api\//] で除外しています。

まとめ・次のステップ

  • npm create cloudflare で、React の画面と Workers の API が同居したプロジェクトをすぐに作れる
  • npm run deploy だけで、世界中に公開できる(無料プランの範囲)
  • vite-plugin-pwa を足せば、ホーム画面に追加できる PWA になる

次のステップとしては、こんなことができます。

  • 独自ドメインを付ける:Cloudflare で管理しているドメインを Worker に割り当てる
  • データを保存する:KV や D1 を使って、API でデータを読み書きする
  • 定期実行する:Cron Triggers で、毎日決まった時間に処理を動かす

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?