cookies の使い方。読める場所と書ける場所
Next.js の cookies() は await して使い、読むのはサーバー側ならどこでもできますが、書けるのは Server Action と Route Handler だけです。
読む・書く・消す
cookies() は next/headers から読み込み、await してから使います。
import { cookies } from 'next/headers'
export default async function Page() {
const store = await cookies()
const theme = store.get('theme')?.value ?? 'light'
return <p>{theme}</p>
}
実機で、theme=dark; a=1 を送ると get('theme') の値は dark、has('theme') は true、getAll() の名前は theme と a でした。Cookie を送らなければ、get は undefined、has は false でした。
書けるのは Server Action と Route Handler だけ
Cookie を書くには、応答に Set-Cookie を載せる必要があります。そのため set と delete を呼べるのは、Server Action と Route Handler の中だけです。ページや layout の中で呼ぶと、開いたときにエラーになります(Cookies can only be modified の直し方)。
// app/actions.ts
'use server'
import { cookies } from 'next/headers'
export async function saveTheme(formData: FormData) {
const store = await cookies()
store.set('theme', String(formData.get('theme')))
}
実機で、このアクションをフォームから送ると、応答に Set-Cookie: theme=blue; Path=/ が付きました。同じ応答で描き直されたページは、もう新しい値(blue)を読んでいました。JavaScript が無いフォーム送信で確かめています。
何も指定しないと HttpOnly も Secure も付かない
書き方ごとに、応答の Set-Cookie を確かめました。
| 書き方 | Set-Cookie |
|---|---|
| set('theme', 'dark') | theme=dark; Path=/ |
| set('sid', 'abc', { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 3600 }) | sid=abc; Path=/; Expires=…; Max-Age=3600; Secure; HttpOnly; SameSite=lax |
| delete('theme') | theme=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT |
何も指定しないと、付くのは Path=/ だけです。ログインの鍵のように、ブラウザの JavaScript から読まれたくないものには httpOnly: true を付けます。HttpOnly の付いた Cookie は、ページの JavaScript から読めません(MDN の Set-Cookie の説明)。secure: true を付けると、https でしか送られません。
maxAge を付けないと、有効期限の無い Cookie になり、ブラウザを閉じると消えます(MDN)。delete は、値を空にして有効期限を過去にすることで消しています。
読むとページは開かれるたびに描かれる
cookies() を読むページは、ビルドのときには描けません。Cookie は開いた人ごとに違うからです。実機のビルドの一覧でも、cookies() を読むページは ƒ(開かれたときに描く)、読まないページは ○(ビルドのときに描く)でした。
読むところだけを Suspense で囲めば、外側はビルドのときに描いておけます(Next.js のキャッシュ)。
proxy でも読み書きできる
ページを描く前に Cookie を見たいときは、proxy の req.cookies で読み、応答の cookies.set で書けます。
// proxy.ts
import { NextResponse, type NextRequest } from 'next/server'
export function proxy(req: NextRequest) {
const res = NextResponse.next()
if (!req.cookies.has('visited')) res.cookies.set('visited', '1', { maxAge: 60 })
return res
}
実機で、ビルドのときに描いたページ(キャッシュから返るページ)の前でも、proxy は Cookie を読み、Set-Cookie を付けました。ページは描いたものがそのまま使われます。
Client Component では使えない
next/headers はサーバーの機能なので、use client のファイルでは読み込めません(next/headers を Client Component で使えない)。ページ(Server Component)で読み、必要な値だけを props で渡します。
まとめ
await cookies()で読む。get・has・getAllはサーバー側ならどこでも使えるset・deleteは Server Action と Route Handler の中だけ- 見られたくない値には
httpOnly: true、https だけならsecure: true - 読むページは開かれるたびに描かれる。ビルドのときに描きたい部分とは分ける
出典
- nextjs.org/docs/app/api-reference/functions/cookies
- nextjs.org/docs/app/api-reference/file-conventions/proxy
- developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。