TL;DR

2026-04-22 深夜、Clerk dev instance (pk_test_) が本番ドメインで after_sign_in_url を既存セッション時に無視する既知バグに詰まり、Better Auth + Neon Postgres (AWS Singapore) に 5 時間で全面移行。Organization プラグインで招待 UI まで配線。結論: 小〜中規模 B2B SaaS なら Clerk より Better Auth が筋が良い。理由は (1) 永続無料 OSS、(2) データ主権 (自社 Postgres)、(3) Auth.js v5 公式後継、(4) 移行コスト低。Next.js 15 の cookies().set() 制約には Route Handler 化で対応。

5h
深夜ワンショットで Clerk → Better Auth 全面移行
Source · mixednuts 2026-04-22 実施

なぜ Clerk をやめたのか

結論

Clerk dev instance が本番ドメインで after_sign_in_url を無視する既知バグ(GitHub issue #2595)に詰まり、Better Auth + Neon Postgres に全面移行した実装ログ。小〜中規模 B2B SaaS では、Clerk の MAU 課金 + ロックイン回避のため Better Auth が筋が良い。移行判断軸は 5 項目のチェックリストで機械的に判定可能。

mixednuts-inc.com のクライアント向けダッシュボードを立ち上げるとき、最初の選択肢は Clerk でした。ドキュメントの厚さ、UI コンポーネントの完成度、Google OAuth 配線の簡便さ。Auth.js v5 よりも運用が楽そうだと判断しました。

問題は dev instance (pk_test_) の本番挙動 にありました。

  • Clerk の pk_test_ インスタンスは本番ドメイン (mixednuts-inc.com) で完全サポートされない
  • Account Portal の after_sign_in_url既存セッション時に無視される 既知挙動 (GitHub issue #2595)
  • 結果として、ログイン後に /user で詰まり、クライアント側ダッシュボードに辿り着けない

Production instance (pk_live_) に切り替えれば解決しますが、それには:

  1. 専用ドメイン (clerk.mixednuts-inc.com, accounts.mixednuts-inc.com 等) の CNAME 4-5 本を DNS に追加
  2. 有料プラン移行 ($25/月〜、MAU 課金発生)
  3. Lock-in の深化 (Clerk Organizations, Clerk Webhooks, etc.)

対して Better Auth は:

  1. 永続無料 OSS — コードが自分のリポジトリにある
  2. データ主権 — セッション・ユーザー・組織情報は自社 Neon Postgres に格納される
  3. Auth.js v5 公式後継 (2025/9 〜) — エコシステムとして持続性が担保される
  4. ドメイン検証不要 — 自前のアプリサーバーがそのまま認証サーバーになる

この 4 点が決定打でした。「Clerk の有料化コスト + Lock-in を回避するための一時コスト」として深夜 5 時間を投下。

移行判断軸 — 5 つのチェックリスト

認証プロバイダーを乗り換えるかどうか、以下の 5 項目で機械的に判定できます。

PRINCIPLE 01

1 MAU あたりのコストが月 $1 を超えているか、超える見込みか

Clerk は Free 枠 10,000 MAU 超で MAU 課金。小規模でも $25/月のミニマムがかかり、スケールすると予想外のコストに。Better Auth は Neon Postgres の Free 枠 (500MB, 191 compute-hour/月) で数千 MAU まで実質無料。

PRINCIPLE 02

ユーザーデータを自社 DB に置きたい要件があるか

freee 経理データ、CRM、分析基盤との JOIN を SQL で一発で回したい場合、Clerk の外部 DB 同期は余計な一段になります。Better Auth なら同じ Postgres 内で JOIN user ON invoice.user_id = user.id が直接書ける。データ主権は長期的に巨大な差になります。

PRINCIPLE 03

Organizations / Multi-tenant 要件があるか

Clerk Organizations は有料プラン。Better Auth の Organization プラグインは無料で、招待 → 自動組織作成 → 招待リンク発行 → 取消まで標準実装されています。B2B SaaS では必須機能。

PRINCIPLE 04

開発者 1〜3 名で運用するか

Clerk の強みは「UI コンポーネントをそのまま使えば認証画面が完成する」点。逆に小規模チームでは、カスタマイズ要件が増えたときに Clerk の抽象化が足枷になります。Better Auth は React コンポーネントを自分で書くことになるが、小規模チームには「薄くて分かる」方が保守しやすい。

PRINCIPLE 05

Lock-in を許容できるか

Clerk → 他社への移行は User テーブルを丸ごと移す必要があり、実質ロックされます。Better Auth は Drizzle ORM + Postgres なので、他の認証ライブラリ (Auth.js, Lucia 等) にも乗換可能。

3 つ以上 Yes なら Better Auth 推奨。mixednuts は 5 つ全て Yes でした。

実装 — 深夜 5 時間のタイムライン

Phase 1 (0:00–1:30): Neon + Drizzle スキーマ

Neon (AWS ap-southeast-1 Singapore, 500MB Free) でインスタンス作成。Drizzle ORM で Better Auth が要求するテーブル (user, session, account, verification, organization, member, invitation) を定義し、drizzle-kit push で Neon に反映。

DATABASE_URLBETTER_AUTH_SECRET (ランダム 32 byte hex) を Vercel env に登録。

Phase 2 (1:30–3:00): Better Auth サーバー & クライアント

src/lib/auth.ts で Better Auth サーバーを定義。Google OAuth + Email/Password + admin + organization の 4 プラグインを有効化。

// src/lib/auth.ts (抜粋)
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { admin, organization } from "better-auth/plugins";
 
export const auth = betterAuth({
  database: drizzleAdapter(db, { provider: "pg" }),
  emailAndPassword: { enabled: true },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_OAUTH_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET!,
    },
  },
  plugins: [admin(), organization()],
});

src/lib/auth-client.tscreateAuthClientadminClient + organizationClient を合成。/api/auth/[...all]/route.ts に BA のハンドラーを mount。

Phase 3 (3:00–4:00): middleware + /login UI 書き換え

middleware から @clerk/nextjs の wrapper を削除し、mn_session cookie + Basic Auth の二段ゲートだけに。/login ページを authClient.signIn.social({ provider: "google" }) に置き換え。

Phase 4 (4:00–5:00): Organization 招待 UI

/dashboard/admin/invites 画面を作成。管理者がメールアドレスを入力 → authClient.organization.inviteMember() → 招待リンク発行 → コピーボタン → 取消機能まで。自動組織作成ヘルパー (auto-org-create) を Server Action で実装。

Phase 5 (5:00 直前): Clerk の完全撤去

npm uninstall @clerk/nextjs
rm -rf src/app/(auth) src/lib/clerk-url.ts
# Vercel env から CLERK_* 3 本を削除 (API 経由)

本番デプロイ → /login 200 → /dashboard/admin/invites 401 → admin 認証で 200。動作確認終了、5 時間で完了

Next.js 15 の罠 — cookies().set() 制約

Better Auth 本体の実装はほぼスムーズだったのですが、1 点だけ詰まりました。

OAuth コールバック後の /login/success ページで、auth.api.getSession() の結果を独自の mn_session cookie にも書き出す処理が必要でした。当初 Server Component として実装したところ、Next.js 15 から cookies().set() が Server Component では禁止 になっていて失敗。

解決は Route Handler 化:

// src/app/login/success/route.ts
export async function GET(req: Request) {
  const session = await auth.api.getSession({ headers: req.headers });
  const response = NextResponse.redirect(new URL("/dashboard", req.url));
  response.cookies.set("mn_session", signSessionJwt(session), { httpOnly: true, secure: true });
  return response;
}

Route Handler では NextResponse.cookies.set() が使えます。Server Component ではなく Route Handler を使う、というのが Next.js 15 以降の原則。

振り返り — 移行を即決できた理由

深夜 5 時間で全面移行を決められたのは、Clerk の問題が 「設定を詰めれば直る」系ではなく「設計思想由来の既知挙動」 だったから。同じ理由で、他の認証ライブラリが破綻したときも移行判断は早いほど得です。

判断の原則:

  1. 問題がドキュメント化されている (GitHub issue で追えている) なら、解決コストは可視化できる
  2. 解決コストが乗換コストを超えるなら、乗り換えたほうが長期的に安い
  3. Lock-in が深い技術は「辞めるときの痛み」で判断する

FAQ

Q. Better Auth は本番環境で使っていいのか? A. 2025/9 の v1 安定版リリース後、本番投入事例が増加しています。Auth.js v5[2] の公式後継として Auth.js コアチームが支持している点も大きい。ただし Clerk のような 24/7 サポートは期待できないので、認証障害時に自力で調査できる DevOps スキルは必要。

Q. Clerk から Better Auth へユーザーデータを移行するには? A. Clerk の User Export API で JSON をダウンロード → password_hash は Clerk が bcrypt ラップしているので、Better Auth の account テーブルに providerId: "credential" で流し込めば OK。ソーシャルログインのみのユーザーは再認証で自動リンクされる。

Q. Neon じゃなくて Supabase でも動く? A. 動きます。Better Auth は Postgres 互換なら何でも OK。Supabase は Auth 機能が別にあるので二重管理になりますが、データベースだけ使うのは問題なし。Neon を選んだのは Free 枠の compute-hour が長めで、AWS Singapore リージョンが東京レイテンシ ~50ms で実用的なため。

Q. Google OAuth の reverse proxy 構成 (www → apex) で動かない問題は? A. BETTER_AUTH_URL を apex ドメイン固定 (https://mixednuts-inc.com) に env 設定し、Google Console に https://mixednuts-inc.com/api/auth/callback/google + https://www.mixednuts-inc.com/api/auth/callback/google 両方を承認済み URI に登録。www → apex の 301 redirect は Vercel domain API で設定。

Q. Clerk の UI コンポーネント (SignIn, UserProfile) の代替は? A. Better Auth は UI コンポーネントを提供しないので、自前で React 書く前提。ただし @better-auth/ui という OSS コンポーネントライブラリが育ってきています。もしくは shadcn/ui + Tailwind で 30 分で組める。


参考文献 / Sources

Better Auth 公式情報:

Clerk 関連の既知挙動:

Neon Postgres[3]:

Next.js 15 cookie 制約:

AI-first 組織の構築にご関心ありませんか?

私たちの知見をあなたの事業に実装します。60分の無料相談をご予約ください。

無料相談を申し込む →