1. はじめに
Nuxtで開発していると、
- 更新したのに画面が変わらない
- APIの結果が古いまま
- 本番だけ挙動が違う
といったキャッシュ絡みの問題に遭遇することがあります。
本記事では、Nuxt(主にNuxt3)におけるキャッシュの仕組みと実務での対処法を整理します。
2. Nuxtのキャッシュはどこに存在するか
Nuxtのキャッシュは複数のレイヤーに分かれています。
キャッシュの種類
| レイヤー | 内容 |
|---|---|
| ブラウザ | HTTPキャッシュ / Service Worker |
| CDN | Cloudflare / Vercel など |
| サーバー(Nitro) | APIレスポンスキャッシュ |
| アプリ内部 | useAsyncData / useFetch |
重要なのは「どのレイヤーでキャッシュされているか」を切り分けることです。
3. useAsyncData / useFetch のキャッシュ
Nuxt3ではデータ取得に useAsyncData や useFetch を使用します。
基本例
const { data } = await useAsyncData('posts', () => $fetch('/api/posts'))
- クライアント遷移時は再取得されない
- 同じキー('posts')はキャッシュされる
キャッシュを無効にする
const { data } = await useFetch('/api/posts', {
cache: 'no-cache'
})
手動で再取得
const { refresh } = await useAsyncData(...)
refresh()
動的キーでキャッシュ制御
const route = useRoute()
const { data } = await useAsyncData(
() => `posts-${route.params.id}`,
() => $fetch(`/api/posts/${route.params.id}`)
)
4. サーバーキャッシュ
キャッシュ設定
export default defineNuxtConfig({
routeRules: {
'/api/posts': { cache: { maxAge: 60 } }
}
})
→ 60秒キャッシュされる
キャッシュ無効化
'/api/posts': { cache: false }
ISR(Incremental Static Regeneration)
'/blog/**': { isr: 60 }
→ 60秒ごとに再生成
5. HTTPキャッシュ(ブラウザ / CDN)
APIレスポンスにヘッダーを設定することで制御できます。
設定例
export default defineEventHandler((event) => {
setHeader(event, 'Cache-Control', 's-maxage=60, stale-while-revalidate')
return { message: 'hello' }
})
よく使う設定
-
no-cache:毎回サーバー確認 -
no-store:キャッシュしない -
max-age:ブラウザキャッシュ -
s-maxage:CDNキャッシュ -
stale-while-revalidate:古いデータを返しつつ裏で更新
6. よくあるハマりポイント
APIは更新しているのに画面が変わらない
原因
- useAsyncDataのキャッシュ
- Nitroキャッシュ
- CDNキャッシュ
対策
- キーを変更する
- refresh()を使う
- cache: false にする
本番だけ更新されない
原因
- CDNキャッシュ(Cloudflare / Vercel)
対策
- Cache-Controlを確認
- キャッシュパージ
ページ遷移でデータが変わらない
原因
- useAsyncDataのキーが固定
対策
const route = useRoute()
const { data } = await useAsyncData(
() => `posts-${route.params.id}`,
() => $fetch(`/api/posts/${route.params.id}`)
)
8. まとめ
Nuxtのキャッシュは以下の4層で考えると理解しやすいです。
- ブラウザ
- CDN
- サーバー(Nitro)
- アプリ内部(useAsyncData)