「一般ユーザーがGoogleで登録・ログインできて、マイページや会員限定コンテンツがある」——そんな本格的な会員サイトを、サーバーを持たずに作れるのが Cloudflare Workers + Google OAuth の組み合わせです。
前記事の Cloudflare AccessでGoogle認証の限定公開サイトを作る は「許可した人だけ入れるゲート」でしたが、本記事は「誰でもGoogleで登録でき、会員ごとにデータを持てる」本格的な会員サイトを自作するアプローチです。基礎となるWorkersは Cloudflare Workers入門 をあわせてどうぞ。
[目次を開く]
作るものの全体像
Workers上で動く軽量フレームワーク Hono を使い、Google OAuthでログインして、会員限定ページを保護する仕組みを作ります。役割分担は次のとおりです。
| 要素 | 役割 |
|---|---|
| Workers + Hono | OAuthフロー・ルーティング・ページ生成 |
| Google OAuth | 認証(「Googleでログイン」) |
| D1 | 会員データ(ユーザーテーブル) |
| KV | セッション保存 |
| R2 | 会員限定のファイル配信(任意) |
ログインの流れは一般的なOAuthと同じです。
ユーザー → /login → Googleの同意画面 → /callback
→ ユーザー情報を取得 → D1に保存 → セッション発行(KV+Cookie)
→ 以降はセッションで会員ページを表示 事前に準備するもの
- Node.js と Cloudflareアカウント(Workersが使える状態)
- Google Cloud Console で作成するOAuthクライアント(client_id / client_secret)
- wrangler(Workers CLI)
ステップ1:Google OAuthクライアントを作る
Google Cloud Console の「APIとサービス → 認証情報 → OAuthクライアントID」でウェブアプリケーションを作成します。承認済みのリダイレクトURIに、開発用と本番用を登録します。
# 開発
http://localhost:8787/callback
# 本番
https://あなたのドメイン/callback 取得した client_id / client_secret は後でWorkersのシークレットに登録します。
ステップ2:プロジェクトとバインディング
Workersプロジェクトを作り、HonoとOAuth補助ライブラリを導入します。
npm create cloudflare@latest my-members -- --framework=hono
cd my-members
npm i @hono/oauth-providers wrangler.jsonc に、会員DB(D1)とセッション用KVをバインディングします。
{
"name": "my-members",
"main": "src/index.ts",
"compatibility_date": "2026-06-01",
"d1_databases": [
{ "binding": "DB", "database_name": "members", "database_id": "<作成したID>" }
],
"kv_namespaces": [
{ "binding": "SESSIONS", "id": "<作成したID>" }
]
} D1とKVは事前に作成しておきます。
npx wrangler d1 create members
npx wrangler kv namespace create SESSIONS ステップ3:シークレットを登録する
client_id / client_secret はコードに直書きせず、Workersのシークレットに登録します。
npx wrangler secret put GOOGLE_CLIENT_ID
npx wrangler secret put GOOGLE_CLIENT_SECRET ステップ4:Googleログインを実装する
Honoの Google OAuth ミドルウェアを使うと、コールバック処理の大半を任せられます。ログイン成功後に、ユーザーをD1に保存し、セッションを発行します。
import { Hono } from 'hono'
import { googleAuth } from '@hono/oauth-providers/google'
import { setCookie } from 'hono/cookie'
const app = new Hono()
app.use('/login', (c, next) =>
googleAuth({
client_id: c.env.GOOGLE_CLIENT_ID,
client_secret: c.env.GOOGLE_CLIENT_SECRET,
scope: ['openid', 'email', 'profile'],
redirect_uri: new URL('/callback', c.req.url).toString(),
})(c, next)
)
// コールバック(googleAuthがトークン交換を処理し、ユーザー情報を渡してくれる)
app.get('/callback', async (c) => {
const user = c.get('user-google') // { email, name, ... }
if (!user?.email) return c.text('認証に失敗しました', 401)
// 会員をD1にupsert
await c.env.DB.prepare(
'INSERT INTO users (email, name) VALUES (?, ?) ' +
'ON CONFLICT(email) DO UPDATE SET name = excluded.name'
).bind(user.email, user.name ?? '').run()
// セッションをKVに保存し、Cookieを付与
const sid = crypto.randomUUID()
await c.env.SESSIONS.put(`sess:${sid}`, user.email, { expirationTtl: 60 * 60 * 24 * 7 })
setCookie(c, 'sid', sid, { httpOnly: true, secure: true, sameSite: 'Lax', path: '/' })
return c.redirect('/members')
})
export default app 事前にD1にusersテーブルを作っておきます。
CREATE TABLE IF NOT EXISTS users (
email TEXT PRIMARY KEY,
name TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
); ステップ5:会員限定ページを保護する
セッションCookieを検証するミドルウェアを作り、会員ページに適用します。未ログインなら /login へリダイレクトします。
import { getCookie } from 'hono/cookie'
const requireAuth = async (c, next) => {
const sid = getCookie(c, 'sid')
const email = sid ? await c.env.SESSIONS.get(`sess:${sid}`) : null
if (!email) return c.redirect('/login')
c.set('email', email)
await next()
}
app.get('/members', requireAuth, (c) => {
return c.html(`<h1>会員ページ</h1><p>ようこそ ${c.get('email')} さん</p>`)
}) これで、/members はログイン済みの会員だけが見られるページになりました。
ステップ6:デプロイと動作確認
# ローカル確認
npx wrangler dev
# 本番公開
npx wrangler deploy 本番公開後は、Google側のリダイレクトURIに本番ドメインの /callback が登録されているか必ず確認しましょう(ミスマッチが最頹のつまずきです)。
会員限定ファイルをR2で配信する(任意)
PDFや動画などの会員限定コンテンツを配るなら、R2と組み合わせます。リクエストを requireAuth で守ってからR2からファイルを返せば、ログイン済みの会員だけがダウンロードできます(R2の詳細は Cloudflare R2入門 を参照)。
app.get('/members/files/:key', requireAuth, async (c) => {
const obj = await c.env.MY_BUCKET.get(c.req.param('key'))
if (!obj) return c.notFound()
return new Response(obj.body)
}) よくあるつまずき(FAQ)
Q. redirect_uri_mismatch エラーが出る
Google側に登録したリダイレクトURIと、アプリが送っているリダイレクトURIが完全一致していないのが原因です。末尾スラッシュや http/https、ポート番号まで揃えましょう。
Q. ローカルでは動くのに本番でエラーになる
シークレット(client_id/secret)が本番に未登録、または本番ドメインのリダイレクトURI未登録が多いです。wrangler secret list で確認しましょう。
Q. ログアウトは?
セッションをKVから削除し、Cookieを無効化します(SESSIONS.delete と Cookieの期限切れ)。
Q. Access(前記事)とどう使い分ける?
「社内・関係者だけの限定公開」ならAccessが手軽で安全です。「一般ユーザーが自由に登録し、会員ごとのデータや課金を持つ」なら本記事の自作アプローチです。
まとめ
Cloudflare Workers と Google OAuth を組み合わせれば、サーバーレスで本格的な会員サイトを自作できます。要点を振り返ります。
- Hono + @hono/oauth-providers で Google OAuth を実装
- 会員は D1、セッションは KV、会員限定ファイルは R2
- client_id/secret は シークレットに、リダイレクトURIは完全一致させる
まずは Hello World のWorkerから始め、ログイン→セッション→会員ページ保護、と段階的に組んでいくのがおすすめです。
あわせて読みたい:
