メインコンテンツへスキップ
← 記事一覧に戻る
·開発·11 min read·有料 ¥500

Supabase RLS で安全な有料コンテンツゲートを作る — 設計パターンと実装の落とし穴

SupabaseセキュリティNext.js個人開発

なぜ RLS が「ペイウォール」の核心なのか

有料コンテンツを配信するサービスで、最も恐ろしいのは何か。

「お金を払っていない人がコンテンツを見られる」状態。

クライアント側で if (purchased) { showContent() } のように制御していると、JavaScript を無効化したり、API を直接叩いたりするだけで突破される。

サーバー側でチェックしても、「全ユーザーの購入履歴テーブル」をうっかり全件取得できる API を作ってしまえば終わり。

Supabase Row Level Security(RLS)は、この問題をデータベース層で解決する。

データベース自体が「このユーザーはこの行を見れる/見れない」を判定するため、アプリケーション側のバグでは突破できない。

このブログの有料記事機能も、RLS を有効にしています。仮に Next.js 側のチェックロジックにバグがあっても、データベース層でブロックされる二重の防御が効きます。

この記事でわかること:

  • RLS の基本概念と「2層防御」の設計思想
  • 購入履歴テーブルの RLS ポリシー設計
  • Service Role Key と Anon Key の使い分け
  • Webhook からの書き込みで RLS をバイパスする正しい方法
  • N+1 問題を避ける購入チェックの実装
  • サーバーコンポーネントでの効率的な使い方

RLS の基本: 「行レベル」のアクセス制御

通常のテーブル権限は「テーブル全体」に対して付与する。

GRANT SELECT ON article_purchases TO authenticated;

これだと、認証済みユーザーは全員の購入履歴を見れてしまう。

RLS は「どの行を見れるか」を SQL で記述できる。

ALTER TABLE article_purchases ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can view own purchases"
  ON article_purchases
  FOR SELECT
  USING (auth.uid() = user_id);

これで、Supabase 経由のクエリは自動的に WHERE user_id = '現在のユーザーID' がついた状態で実行される。

別のユーザーの購入履歴を取得しようとしても、結果は空になる。


関連記事

Next.js + Supabase 個人開発入門2026

無料枠だけでSaaSを作る構成と実装手順を解説

Stripe × Next.js 決済導入ガイド2026

個人開発で売上を立てるまでのStripe実装手順を全公開

個人開発の認証実装完全ガイド

Clerk・Supabase Auth・NextAuthの比較と選び方を解説

Claude Code で副業を始める 6ステップ — 無料

環境構築から最初の売上まで、週1配信で学べる無料メール講座。登録で「収益化チェックリスト 15 項目」もプレゼント。

副業6ステップ講座(無料)

この先は有料コンテンツです

記事の続きを読むには購入が必要です

¥500

この記事が役に立ったらシェア