ニュース

Tatami Study の教室サイトを Cloudflare Workers に移しました

2026年8月、プログラミング教室 Tatami Study の Webサイトの配信先を Vercel から Cloudflare Workers に切り替えました。体験授業の予約機能が Workers 上で動かなかった原因と、その直し方を書きます。

2026年8月24日に、プログラミング教室 Tatami Study の Webサイト(programming.miraiport.com)の配信先を Vercel から Cloudflare Workers に切り替えました。サイトは Next.js で作っていて、Workers 上では OpenNext の Cloudflare アダプタを使って動かしています。

ほとんどのページはそのまま動きましたが、1か所だけ動かないところがありました。体験授業の予約で使っている、空き時間の取得です。

予約の空き時間が取れない

予約画面では、教室の Google カレンダーから空き時間を読み出しています。Google の API を呼ぶにはサービスアカウントでアクセストークンを取る必要があり、移行前は google-auth-library を使っていました。

これが Workers 上では Could not refresh access token で失敗し、空き時間の API がエラーを返していました。

Web Crypto で書き直す

サービスアカウントのトークン取得は、やっていること自体は単純です。

  1. 「誰が・どの権限で」を書いた JWT を作る
  2. 秘密鍵で RS256 署名する
  3. Google のトークンエンドポイントに送って、アクセストークンと交換する

署名は Workers にもある Web Crypto API でできるので、ライブラリをやめて、この部分だけを自分で書くことにしました。

const key = await crypto.subtle.importKey(
  "pkcs8",
  pemToDer(privateKey),
  { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
  false,
  ["sign"]
);

const signature = await crypto.subtle.sign(
  "RSASSA-PKCS1-v1_5",
  key,
  new TextEncoder().encode(`${header}.${claim}`)
);

アクセストークンは1時間有効なので、期限の60秒前までは使い回しています。カレンダー API を呼ぶ側のコードは変えていません。

もう1つの落とし穴

書き直したあとも、今度は atob() called with invalid base64-encoded data で失敗し続けました。

原因はコードではなく、Workers に登録した秘密鍵の値でした。設定ファイルから値をまとめて登録したときに、ファイル内のコメント行と空行が、直前の値の末尾にくっついたまま登録されていたのです。秘密鍵の末尾に日本語のコメントが付いていれば、base64 として読めないのは当然でした。

設定ファイルを読むときは KEY=値 の形の行だけを拾い、コメント行と空行は読み飛ばすようにして、値を入れ直しました。

切り替える前に確かめたこと

切り替えの前に、移行前と移行後で同じ結果が返ってくるかを確かめました。

確かめたもの 結果
全ページの表示テキスト 一致
予約の空き時間 API レスポンスがバイト単位で一致
画像の最適化 動作

こうした確認をしたうえで、本番のドメインを切り替えました。

ほかの記事

すべての記事