← そらいろのねこ

Webサービス つくった人: 森下航

TabascoLove

タバスコ好きが匿名で語り合う場所。ログインは要りません。

実物を見る tabascolove.pages.dev

どうしてつくったか

マスコットのチリ男くん

好きなものの話を、置く場所がなかった

タバスコが好きでも、その話をする場所はないです。SNSに書いても流れていきます。タバスコ好きのアカウントをつくってもよいですが管理が面倒です。なら、いっそのことタバスコ好きが集まるサイトをつくってみよう。人が来なくても自分のメモにもなるしいいかなー。とつくろうと決めました。

4体のマスコット

Cloudflareだけで組んで、途中で見た目を捨てた

CodexのAstraがどの程度のものか試すために作成しました。それなりの見た目のもの、今の原型となるものができました。ただ、どうせなら自分の好きな見た目に変えたいなーと思い、前面をドット絵に変えました。ドット絵もDotCreatorというAIでつくったプロジェクトでつくっています。

ごきげんな姿のハバ子ちゃん

かわいいの声はもらえた。人はまだ来ない

割とかわいいキャラクターができたものの、だれも知らないのでXのポストで何度か紹介をしました。可愛いと言ってくれる方も多くて嬉しかったです。人気投票もしましたが、多くの票は集まりませんでした。まぁそんなもんですよね(ノ∀`)

TabascoLove のトップページ

匿名で書けて、読み物も置いてある

それでもサイトは公開しました。いつでも来れて誰でも匿名で投稿・コメント・いいねができます。おススメの食べ方、おススメしない食べ方、タバスコの愛を語る投稿ができるようになっています。今は私のメモサイトになっていますが、いつか誰かの目に留まることを夢見ています。ちなみにサイトトップマスコットは、朝がチリ男くん、昼がガラちゃん、夜がハバ子ちゃん、深夜がピリ助くんに替わります。気が向いたら訪問してみてください。

設計の全体像

TabascoLove の構成図 ブラウザからCloudflare Pagesへ。静的ファイルはそのまま配られ、/api と /media だけがFunctionsを通ってD1とR2に届く。書き込みはTurnstileの確認を通る。 ブラウザ 読む・書く Cloudflare Pages 静的ファイル HTML・CSS・画像 Functions /api/* /media/* D1 投稿・コメント・いいね R2 画像(公開アクセス無効) Turnstile 書き込みの前に確認 Web Analytics Cookieなし
配るのはPages、受けるのはFunctionsだけ。サーバーは持っていません。

静的なファイルはPagesがそのまま配ります。Functionsが起きるのは /api/*/media/* を呼んだときだけです。投稿・コメント・いいね・通報はD1に入り、画像は公開アクセスを切ったR2に置いて /media/* 経由でだけ出します。書き込みの前にはTurnstileの確認が入り、本番ではhostnameとactionも照らします。

つくりかた

サーバーを持たない構成にした理由

無料枠に収めきることを最初の制約に置きました。Pagesの静的配信を既定にして、Functionsが起きるのは /api/*/media/*、それにOGPを差し込む /posts/*/articles/* だけです。残りはワーカーを通らないので、読むだけの訪問者では課金対象の実行がほぼ発生しません。上限は src/config.ts に定数で置いてあります。1日の操作1,500回、トップの表示20,000回、解析ビーコン10,000回、R2は月5万書き込み・100万読み出しまで。D1の無料枠が1日10万行の書き込みなので、その内側に収まるよう各上限を決めました。

画像をR2に置いて、公開しなかった理由

R2は公開アクセスを切ってあり、画像は /media/<key> 経由でしか出ません。この経路では、まずキー名を ^[\w-]+\.(jpg|jpeg|png|webp)$ で弾き、次にD1へ posts.hidden=0 AND posts.ready=1 を問い合わせ、そこを通ってからR2を読みます。投稿が隠れれば画像も同時に見えなくなる——これが公開バケットを避けた理由です。返すときは Content-Security-Policy: default-src 'none'; sandboxX-Content-Type-Options: nosniff を付けて、アップロードされた画像がページとして解釈される余地を消しています。

荒れへの備えと、AIとの分担

匿名のまま荒れを抑えるため、判定を機械側に寄せました。通報は接続元のハッシュで数え、reportThreshold: 3——異なる3つの接続元から集まった時点で自動的に隠れます。人が見るのはそのあとです。Turnstileの検証はヘッダのトークンで先に済ませ、画像の本文を読む前に弾きます。3枚×2MiBを受け取ってから捨てるのは無駄なので、この順番にしました。

作り方は3手でした。初期実装はCodexのAstraに出させ、動く原型を2日で得ています。そこからデザイン刷新・GIF対応・アクセス解析はClaude Codeで進め、マスコットのドット絵は別プロジェクトのDotCreatorで生成しました。人間が持っているのは方針の決定と検収です。どんな場所にするか、無料枠のどこに線を引くか、見た目を作り直すか。判断の結果は docs/design.md に日付つきの検証ログとして8本残してあります。

構成: Cloudflare Pages(Advanced mode・direct upload)/ D1 / R2 / Turnstile。テストは Vitest・node:test・Playwright の13ファイル。

数字と運用

開発期間
7日(2026-09-09〜09-15)
コミット
21
読みもの
8
D1マイグレーション
8
テスト
13ファイル
マスコットのGIF
32本(8,309,963バイト)

運用に人の手はほとんど要りません。デプロイは npm run deploy の直接アップロードで、GitHubへpushしても本番は動きません。定期実行は置かず、認証済みの操作が起きたときに期限切れのカウンターを最大100件ずつ掃除します。通報が3つの接続元から集まれば、人が見る前に自動で隠れます。

数字の出どころは sorairo-neko-survey.md(2026-09-20〜21の調査)と、GIFの容量は2026-09-22にリポジトリで測り直した値です。

関連リンク

次のプロジェクト ひなたカレンダー →