この記事は約 5 分で読めます。
筆者プロフィール: ソフトウェアエンジニア。「知った気にならない。いつまでも学び続ける」を信条に、業務と個人開発の両輪で技術を磨いています。AI 駆動開発で複数の個人開発アプリを構築・運用中。
👉 ポートフォリオ: 筆者ホームページ
NextAuth v5 (Auth.js) を Netlify にデプロイすると、useSession().update() が動かない という罠に遭遇します。本記事では、運用中の SaaS 「たすきば Knowledge Relay」 での回避策を整理します。
サービスの機能紹介・画面イメージ・コンセプトは公式プロダクトページをご覧ください。
👉 たすきば Knowledge Relay — 公式プロダクトページ
こんなコード、書いたことありませんか?
// Vercel では正常動作するコード
'use client';
import { useSession } from 'next-auth/react';
export function ProfileImageUploader() {
const { update } = useSession();
async function handleUpload(file: File) {
await uploadProfileImage(file);
await update(); // セッション再生成 → 画面に新しい画像が反映
}
}
これを Netlify にデプロイすると、update() 後に画面が更新されません。
1. 何が起きていたか
ブラウザの DevTools で確認すると:
| 観測 | 状態 |
|---|---|
update() の API リクエスト送信 |
正常 |
| API レスポンス | 200 で返っている |
| Set-Cookie ヘッダ | 応答から脱落 |
結果として、新しい JWT が cookie に書き込まれず、セッション状態が古いまま残る という現象が起きます。
2. 原因: Netlify の Set-Cookie 脱落
Netlify は、Next.js のレスポンスを内部の Edge Function 経由でユーザに返します。このとき、特定の条件下で Set-Cookie ヘッダが応答パイプラインから脱落する ことがあります。
具体的には:
-
useSession().update()から呼ばれる internal API route (/api/auth/session) が Set-Cookie を返す - Netlify Edge Function が、これを次の layer に正しく転送できない
公式 issue でも報告されているが、根本対応は時間がかかりそうでした。本番リリースを目前に控え、回避策を取る方針 にしました。
3. 回避策 1: テーマは専用 cookie に分離
ユーザがダーク / ライトテーマを切り替える機能。当初 NextAuth セッションの user.theme に格納していましたが、Set-Cookie 脱落で更新されない。
対応: NextAuth セッションから切り離し、専用 cookie (tasukiba_theme) を直接書く ことに。
// src/lib/theme-cookie.ts
import { cookies } from 'next/headers';
export async function setThemeCookie(theme: 'light' | 'dark') {
const cookieStore = await cookies();
cookieStore.set('tasukiba_theme', theme, {
path: '/',
maxAge: 60 * 60 * 24 * 365,
sameSite: 'lax',
});
}
cookie 書き込みは Server Action 内で行います。NextAuth と完全に独立しているため、Set-Cookie 脱落の影響を受けない のがポイント。
結果として、テーマ切り替えは即座に画面に反映されます。
4. 回避策 2: MFA / TZ / Locale は JWT 再署名ヘルパで書く
MFA 設定変更、タイムゾーン変更、Locale 変更などの セッションに直接乗せる必要がある情報 は、update() ではなく JWT を再署名する独自ヘルパ で対処します。
// src/lib/auth-jwt-helper.ts
import { cookies } from 'next/headers';
import { encode } from 'next-auth/jwt';
export async function resignJwt(
patches: Partial<{ mfaEnabled: boolean; tz: string; locale: string }>,
) {
const current = await getCurrentJwt();
const newPayload = { ...current, ...patches };
const token = await encode({
token: newPayload,
secret: process.env.NEXTAUTH_SECRET!,
salt: 'authjs.session-token',
});
const cookieStore = await cookies();
cookieStore.set('authjs.session-token', token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
});
}
| ポイント | 内容 |
|---|---|
next-auth/jwt の encode
|
JWT を自前で再署名 |
cookies().set() |
Server Action 内で直接書き込む (Set-Cookie パイプラインを経由しない) |
| 結果 | Netlify の Set-Cookie 脱落の影響を受けない |
このヘルパを「MFA 有効化」「タイムゾーン変更」「言語切り替え」の場面で使います。
5. 回避策 3: 新規コードで useSession().update() を使わない
新規実装では、原則として useSession().update() を使わない方針にしました。
代わりに:
| 用途 | 採用するアプローチ |
|---|---|
| 表示更新が必要 |
router.refresh() で Server Component を再評価 |
| セッション情報を変えたい | JWT 再署名ヘルパ |
| 軽量な UI 状態 (テーマ等) | 専用 cookie |
6. "再ログインで対応する" がダメな理由
「いったんログアウトして再ログインすれば、新しいセッションが取れる」は事実です。しかし、これは UX として明確に悪い。
ユーザが「テーマを変えただけ」なのに「再ログインしてください」と言われる体験は、信頼を損ないます。特にビジネス利用者は、ログイン状態を頻繁に切らされることに非常に neg な感情 を持ちます。
—— だからこそ、回避策を施してでも update() 相当の体験を実現する必要がありました。
7. Cookie 削除に依存しない設計へ
この問題は、サインアウト時にも影響します。
NextAuth の signOut() は、JWT cookie を削除することでログアウトを実現します。しかし Netlify の Set-Cookie 脱落で delete cookie ヘッダも脱落する ことがある。
結果として「サインアウトしたはずなのにログイン状態が残る」事故が起きる可能性 (severity-1)。
たすきばはこれを構造的に防ぐため、サインアウトを Cookie 削除に依存させない 設計にしました (次回 E-4 で詳説)。
8. 学んだこと — PaaS の挙動を疑う習慣
外部 PaaS の挙動は、ライブラリの想定と必ずしも一致しません。
特に Set-Cookie のような HTTP の根本機能 であっても、PaaS の middleware / Edge Function 層で脱落する可能性があります。
ライブラリ (NextAuth) のドキュメント通りに書いて動かないとき、犯人がライブラリではなく PaaS の挙動 であることはよくあります。
このとき「ライブラリの不具合だ」と決めつけず、HTTP レベルで何が起きているか を DevTools で確認する習慣が、原因究明の鍵になります。
おわりに
| 機能 | NextAuth update()
|
たすきばの対応 |
|---|---|---|
| テーマ切り替え | ❌ Netlify で動かない | 専用 cookie tasukiba_theme
|
| MFA 有効化 | ❌ 同上 |
src/lib/auth-jwt-helper.ts で JWT 再署名 |
| TZ / Locale 変更 | ❌ 同上 | 同上 |
| 表示更新 | ❌ 同上 | router.refresh() |
| サインアウト | ❌ Cookie 残留リスク |
tokenVersion increment + layout DB 照合 (次回 E-4) |
新規コードでは useSession().update() を使わないルールとして、CONTRIBUTING.md にも明記しています。
NextAuth v5 + Netlify を使う方は、最初からこの罠を回避する設計 をおすすめします。
本記事の罠と回避策は、運用中の SaaS 「たすきば Knowledge Relay」 で実装したものです。
👉 たすきば Knowledge Relay — 公式プロダクトページ