- Nuxt
Nuxt SSR・SSGの設定|routeRules全オプションとヘッダー実測
Nuxtのレンダリング方式は、nuxt.config.ts の routeRules で「ページ(URL)ごと」に選べます。 トップページはビルド時に静的HTMLにし(SSG)、記事一覧は数秒〜数分キャッシュし(SWR・ISR)、管理画面はブラウザだけで描画する(SPA)、といった組み合わせを1つのアプリで実現できます。これを ハイブリッドレンダリング と呼びます。
この記事では、Nuxt 4.5.2(Nitro 2.13.4) で、各方式の違いと選び方を表にまとめ、routeRules の 全オプションを型定義から確認した早見表 とコピペ用の設定を載せます。さらに、実際に nuxt build / nuxt generate を実行した出力、curl -I で取得した各ルートの cache-control などのヘッダー、SWRで表示時刻が更新される間隔の実測ログ、Vercel・Netlify向けにビルドしたときのISR設定ファイルまで、実行結果つきで確認します。
Nuxtのレンダリング方式 早見表
| 方式 | routeRules の書き方 | HTMLを作るタイミング | キャッシュ | 向いているページ |
|---|---|---|---|---|
| SSR(サーバーサイドレンダリング) | 指定なし(既定) | リクエストのたび | なし | 会員ごとに内容が変わるページ、在庫など常に最新が必要なページ |
| SPA(クライアント描画) | { ssr: false } |
ブラウザでJSが実行されたとき | なし | 管理画面、ログイン後のダッシュボード |
| SSG(事前レンダリング) | { prerender: true } |
nuxt build のとき |
静的ファイル | トップ、会社概要、規約など更新が少ないページ |
| SWR | { swr: 秒数 } |
初回アクセス時+期限切れ後に裏で再生成 | Nitroのサーバー内キャッシュ+s-maxage |
記事一覧、ランキングなど数秒〜数分の遅れを許せるページ |
| ISR | { isr: 秒数 } |
初回アクセス時+期限切れ後に再生成 | 対応ホスティングのCDN | Vercel・Netlifyでの記事ページ、商品ページ |
| cache | { cache: { maxAge: 秒数 } } |
期限切れ後の最初のリクエストで作り直す | Nitroのサーバー内キャッシュ+max-age |
古い版を返したくないが、短時間はキャッシュしたいページ |
- 迷ったら「既定はSSR、更新が少ないページだけ
prerender: true」から始めるのが安全です。 - SWRとISRの違いは「どこにキャッシュするか」。SWRはNitroサーバー、ISRはホスティングのCDNです。Node.jsサーバーで動かす場合、
isrはキャッシュされず SSRと同じ動き になりました(後述の実測)。 nuxt generateで全ページを静的化すると、ハイブリッドレンダリング(SWR・ISR・SSR)は使えません(公式ドキュメントに明記)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Nuxt SSR・SSGの設定|routeRules全オプションとヘッダー実測」
検証環境
| 項目 | バージョン |
|---|---|
| Nuxt | 4.5.2 |
| Nitro | 2.13.4(preset: node-server) |
| Vue / Vite | 3.5.43 / 8.3.1 |
| Node.js | 22.22.2(Nuxt 4.5 は ^22.19 以上が必要) |
プロジェクトは npm install nuxt@4.5.2 で作った最小構成です。各ページで /api/now(現在時刻を返すAPI)を useFetch し、HTMLに埋め込まれた時刻=そのHTMLが作られた時刻 として観察しました。
// server/api/now.get.ts
export default defineEventHandler(() => ({ now: new Date().toISOString() }))
<!-- app/pages/swr.vue(ssr.vue・isr.vue なども同じ中身) -->
<script setup lang="ts">
const { data } = await useFetch('/api/now', { key: 'swr' })
</script>
<template>
<main>
<h1>swr</h1>
<p id="rendered">{{ data?.now }}</p>
</main>
</template>
どの方式を選ぶか(判断表)
判断の軸は「内容が人によって変わるか」「どれくらい古くてもよいか」「どこにデプロイするか」の3つです。
flowchart TD
A[そのページはログインした人ごとに内容が変わる?] -->|はい| B{検索エンジンに載せる?}
B -->|いいえ| SPA["ssr: false(SPA)"]
B -->|はい| SSR["指定なし(SSR)"]
A -->|いいえ| C{内容はビルドのときに決まる?}
C -->|はい| SSG["prerender: true(SSG)"]
C -->|いいえ| D{数秒〜数分古くてもよい?}
D -->|いいえ| SSR
D -->|はい| E{Vercel / Netlify にデプロイ?}
E -->|はい| ISR["isr: 秒数"]
E -->|いいえ| SWR["swr: 秒数"]
| 状況 | おすすめ | 理由 |
|---|---|---|
| 会社概要・規約・LP | prerender: true |
サーバー処理ゼロ。CDNからそのまま配信できる |
| ブログ記事(Node.jsで運用) | swr: 600 など |
初回以降はキャッシュから即返し、裏で最新化 |
| ブログ記事(Vercel / Netlify) | isr: 600 など |
CDNにキャッシュされ、関数の起動回数も減る |
| 在庫数・予約枠など常に最新 | 指定なし(SSR) | キャッシュすると古い数字が表示される |
| 管理画面 | ssr: false |
検索エンジン向けのHTMLが不要。サーバー負荷も減る |
| 数秒だけキャッシュし、古い版は返したくない | cache: { maxAge: 5, swr: false } |
期限切れ時は作り直してから返す |
routeRules の全オプション早見表
Nuxt 4.5.2 で使える routeRules のキーを、Nitroの型定義(NitroRouteConfig)とNuxtの型拡張から確認した一覧です。
| オプション | 型 | 役割 | 例 |
|---|---|---|---|
prerender |
boolean |
ビルド時に静的HTMLを作る | '/': { prerender: true } |
ssr |
boolean |
false でサーバー描画をやめ、SPAにする |
'/admin/**': { ssr: false } |
swr |
boolean | number |
Nitroでキャッシュし、期限後は古い版を返しつつ再生成 | '/news': { swr: 60 } |
isr |
boolean | number | object |
対応ホスティングのCDNでキャッシュ(true は期限なし) |
'/blog/**': { isr: 3600 } |
cache |
object | false |
Nitroのキャッシュを細かく指定(maxAge、swr、staleMaxAge、varies など) |
{ cache: { maxAge: 5, swr: false } } |
headers |
Record<string, string> |
レスポンスヘッダーを追加 | { headers: { 'x-robots-tag': 'noindex' } } |
redirect |
string | { to, statusCode } |
リダイレクト(文字列だけなら 307) | { redirect: { to: '/new', statusCode: 301 } } |
cors |
boolean |
CORSヘッダー4つを自動追加 | '/api/**': { cors: true } |
proxy |
string | { to, ...} |
別のURLへ転送 | '/proxy/**': { proxy: 'https://example.com/**' } |
noScripts |
boolean |
NuxtのJSとリソースヒントを出力しない | '/lite': { noScripts: true } |
appMiddleware |
string | string[] | Record<string, boolean> |
Vue側のルートミドルウェアを実行する/しない | { appMiddleware: { auth: false } } |
appLayout |
string | false |
レイアウトをまとめて指定(Nuxt 4.3以降) | '/admin/**': { appLayout: 'admin' } |
static |
boolean | number |
旧来のオプション(Vercel向けの互換用)。新規は isr を使う |
— |
- パスは
/**で配下すべて、/*で1階層だけにマッチします。 streamingというキーも型定義にはありますが、公式ドキュメントのRoute Rules一覧には載っていないため、この記事では扱いません。experimentalNoScriptsは非推奨になり、noScriptsに置き換わっています(型定義の@deprecated表記で確認)。
コピペで使える nuxt.config.ts(ハイブリッドレンダリング)
以下が、この記事の実測に使った設定です。そのまま貼って、パスを自分のサイトに合わせて書き換えれば動きます。
// nuxt.config.ts(Nuxt 4.5.2)
export default defineNuxtConfig({
compatibilityDate: '2026-09-01',
devtools: { enabled: false },
routeRules: {
// SSG:ビルド時にHTMLを作る
'/': { prerender: true },
// SPA:サーバーではHTMLの中身を作らない
'/spa': { ssr: false },
// SWR:10秒キャッシュ、期限切れ後は古い版を返しつつ裏で作り直す
'/swr': { swr: 10 },
// ISR:対応ホスティング(Vercel/Netlify)ではCDNにキャッシュ
'/isr': { isr: 60 },
// cache:Nitroのキャッシュを細かく指定
'/cached': { cache: { maxAge: 5, swr: false } },
// ヘッダーを追加
'/lite': { noScripts: true, headers: { 'x-robots-tag': 'noindex' } },
// リダイレクト(既定は307)
'/old-page': { redirect: '/' },
'/moved': { redirect: { to: '/ssr', statusCode: 301 } },
// API:CORSとキャッシュ無効
'/api/**': { cors: true, headers: { 'cache-control': 'no-store' } },
// プロキシ(ここでは同じサーバーのAPIへ転送)
'/proxy/**': { proxy: '/api/**' },
// 管理画面:SPAにして、レイアウトもまとめて指定
'/admin/**': { ssr: false, appLayout: 'admin' },
},
})
ルールは複数のパターンに当てはまると合成されます。 たとえば '/blog/**': { isr: 3600 } と '/blog/draft/**': { ssr: false } を両方書くと、/blog/draft/1 には両方が適用されます。意図しない組み合わせを避けるには、パターンを重ねすぎないのがコツです。
nuxt build の実行結果:prerender されたファイル
npx nuxt build を実行すると、prerender: true のページだけがビルド中にHTML化されます。
• Nuxt 4.5.2 (with Nitro 2.13.4, Vite 8.3.1 and Vue 3.5.43)
• Nitro preset: node-server
[nitro] i Initializing prerenderer
[nitro] i Prerendering 1 routes
[nitro] ├─ / (90ms)
[nitro] ├─ /_payload.json (6ms)
[nitro] i Prerendered 2 routes in 1.657 seconds
[nitro] √ Generated public .output/public
[nitro] i Building Nuxt Nitro server (preset: node-server, compatibility date: 2026-09-01)
[nitro] √ Nuxt Nitro server built
[nitro] √ You can preview this build using node .output/server/index.mjs
出力された静的ファイル(_nuxt/ 配下のJS・CSSを除く)は次の2つでした。
.output/public/_payload.json
.output/public/index.html
_payload.json は、useFetch で取得したデータの保存ファイルです。別のページから NuxtLink で移動してきたとき、APIを呼び直さずにこのファイルを読みます。
ビルド中のリクエストには x-nitro-prerender ヘッダーが付く
prerender はNitroが自分自身にリクエストを送って行います。サーバーミドルウェアでヘッダーを記録すると、ビルド中だけ x-nitro-prerender リクエストヘッダーが付くことが確認できました。
// server/middleware/prerender-log.ts(確認用)
export default defineEventHandler((event) => {
const h = getRequestHeader(event, 'x-nitro-prerender')
if (h) console.log('[prerender]', event.path, 'x-nitro-prerender =', h)
})
[prerender] / x-nitro-prerender = /
[prerender] /api/now x-nitro-prerender = /
[nitro] ├─ / (366ms)
[prerender] /_payload.json x-nitro-prerender = /_payload.json
[nitro] ├─ /_payload.json (4ms)
ページの中で呼んだ /api/now にも同じヘッダーが付いています。 ビルド時だけ処理を変えたい(外部APIの呼び出しを減らすなど)ときの判定に使えます。なお、配信時のレスポンスにはこのヘッダーは付きません。
prerender の対象を増やす方法
| 方法 | 書き方 | 使いどころ |
|---|---|---|
| routeRules | '/about': { prerender: true } |
パスが決まっているページ |
| nitro.prerender.routes | nitro: { prerender: { routes: ['/items/1', '/items/2'] } } |
動的ルートのURLを列挙する |
| nitro.prerender.crawlLinks | nitro: { prerender: { crawlLinks: true } } |
prerenderしたページのリンクをたどって追加 |
| nitro.prerender.ignore | nitro: { prerender: { ignore: ['/dynamic'] } } |
対象から外す |
| prerenderRoutes() | ページ内で prerenderRoutes(['/items/3']) |
取得したデータからURLを追加する |
curl -I で各ルートのヘッダーを実測
PORT=7450 node .output/server/index.mjs で起動し、各ルートに curl -I を送った結果です(date・connection などの共通ヘッダーは省略)。
| パス | ルール | ステータス | 主なヘッダー |
|---|---|---|---|
/ |
prerender: true |
200 | ETag、Last-Modified(静的ファイルとして配信。x-powered-by なし) |
/ssr |
なし | 200 | x-powered-by: Nuxt のみ。cache-control なし |
/spa |
ssr: false |
200 | x-powered-by: Nuxt のみ |
/swr |
swr: 10 |
200 | cache-control: s-maxage=10, stale-while-revalidate、etag、last-modified |
/isr |
isr: 60 |
200 | x-powered-by: Nuxt のみ(Node.jsでは cache-control なし) |
/cached |
cache: { maxAge: 5, swr: false } |
200 | cache-control: max-age=5、etag、last-modified |
/lite |
noScripts + headers |
200 | x-robots-tag: noindex |
/old-page |
redirect: '/' |
307 | location: / |
/moved |
redirect: { statusCode: 301 } |
301 | location: /ssr |
/api/now |
cors + headers |
200 | access-control-allow-* 3つ、access-control-max-age: 0、cache-control: no-store |
/proxy/now |
proxy: '/api/**' |
200 | 転送先 /api/now と同じJSONとヘッダー |
実際の出力(抜粋)です。
=== /
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
ETag: "726-OrRtedBa7phdcp28EM4mrkFL404"
Last-Modified: Tue, 29 Sep 2026 10:24:19 GMT
=== /swr
HTTP/1.1 200 OK
last-modified: Tue, 29 Sep 2026 10:24:33 GMT
etag: W/"oe1yqmxzb0"
cache-control: s-maxage=10, stale-while-revalidate
content-type: text/html;charset=utf-8
x-powered-by: Nuxt
=== /cached
HTTP/1.1 200 OK
last-modified: Tue, 29 Sep 2026 10:24:33 GMT
etag: W/"SAkkEpjCUQ"
cache-control: max-age=5
content-type: text/html;charset=utf-8
x-powered-by: Nuxt
=== /old-page
HTTP/1.1 307 Temporary Redirect
location: /
content-type: text/html
=== /moved
HTTP/1.1 301 Moved Permanently
location: /ssr
content-type: text/html
swr は s-maxage(CDN・共有キャッシュ向け)、cache は max-age(ブラウザも含む) が付く点が違います。max-age はブラウザ自身もキャッシュするため、更新をすぐ見せたいページでは swr の方が扱いやすいです。
SPA・noScripts ページのHTML
ssr: false のページは、中身が空の <div id="__nuxt"></div> とJSだけが返ります。検索エンジン向けの本文はHTMLに含まれません。
<body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};...</script>
<script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"serverRendered":1},false]</script></body>
noScripts: true のページは逆に、本文はあるが <script> が1つもない HTMLになりました。ボタンなどの操作は動かなくなるので、読むだけのページ向けです。
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head><body><div id="__nuxt"><div><header>default</header><!--[--><main><h1>lite</h1><p id="rendered">2026-09-29T10:24:39.472Z</p></main><!--]--></div></div><div id="teleports"></div></body></html>
SWRで表示時刻が更新される間隔を実測
swr: 10 の /swr に2秒おきにリクエストし、HTMLに埋め込まれた時刻(=HTMLが作られた時刻)を読み取りました。
for i in $(seq 0 12); do
r=$(curl -s localhost:7450/swr | grep -o 'id="rendered">[^<]*' | cut -d'>' -f2)
echo "request $(date -u +%H:%M:%S) -> rendered ${r:11:8}"
sleep 2
done
request 10:24:43 -> rendered 10:24:33
request 10:24:45 -> rendered 10:24:33
request 10:24:47 -> rendered 10:24:45
request 10:24:49 -> rendered 10:24:45
request 10:24:51 -> rendered 10:24:45
request 10:24:53 -> rendered 10:24:45
request 10:24:55 -> rendered 10:24:45
request 10:24:57 -> rendered 10:24:55
request 10:24:59 -> rendered 10:24:55
request 10:25:01 -> rendered 10:24:55
request 10:25:03 -> rendered 10:24:55
request 10:25:05 -> rendered 10:24:55
request 10:25:07 -> rendered 10:25:05
読み取れることは次のとおりです。
- 10秒を過ぎた最初のリクエスト(10:24:45)には古いHTML(10:24:33)が返り、その裏で作り直しが始まる
- 次のリクエスト(10:24:47)から新しいHTML(10:24:45)が返る
- その後も約10秒ごとに更新(10:24:45 → 10:24:55 → 10:25:05)
「利用者を待たせない代わりに、期限切れ直後の1回だけ古い版を見せる」のがSWRです。
SSR・ISR(Node.js)・cache と並べて比較
同じ方法で /ssr・/isr・/cached(maxAge: 5, swr: false)を比べました。
request 10:25:16 ssr=10:25:16 isr=10:25:16 cached=10:25:16
request 10:25:18 ssr=10:25:19 isr=10:25:19 cached=10:25:16
request 10:25:21 ssr=10:25:21 isr=10:25:21 cached=10:25:16
request 10:25:23 ssr=10:25:23 isr=10:25:23 cached=10:25:23
request 10:25:25 ssr=10:25:25 isr=10:25:25 cached=10:25:23
request 10:25:27 ssr=10:25:27 isr=10:25:27 cached=10:25:23
request 10:25:29 ssr=10:25:29 isr=10:25:29 cached=10:25:29
request 10:25:31 ssr=10:25:31 isr=10:25:31 cached=10:25:29
| ルール | 結果 | 解釈 |
|---|---|---|
| なし(SSR) | 毎回新しい | リクエストのたびに描画 |
isr: 60(Node.js) |
毎回新しい | Node.jsサーバーではCDNがないため、キャッシュされない |
cache: { maxAge: 5, swr: false } |
約5〜6秒ごとに更新 | 期限切れ時は古い版を返さず、作り直してから返す |
Node.jsサーバーで「キャッシュしたい」なら isr ではなく swr か cache を使う、というのが実測からの結論です。
ISR・SWRページは _payload.json も作られる
Nuxt 4.3で、ISR・SWR・cache のルートでも ペイロード(_payload.json)が抽出されるようになりました。実際に取得できました。
== /swr/_payload.json
HTTP/1.1 200 OK
cache-control: s-maxage=10, stale-while-revalidate
content-type: application/json;charset=utf-8
[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"swr":3},{"now":4},"2026-09-29T10:25:39.219Z"]
== /ssr/_payload.json
HTTP/1.1 404 Page not found: /ssr/_payload.json
SWRページのHTMLには <link rel="preload" as="fetch" href="/swr/_payload.json?_b=..."> が入り、ページ移動のときにAPIを呼び直さずキャッシュ済みのデータを使います。通常のSSRページには _payload.json はありません。
nuxt generate で全ページを静的化したときの出力
npx nuxt generate(=nuxt build --prerender)を同じ設定で実行すると、ルールに関係なく全ページが静的HTMLになります。
[nitro] i Prerendering 3 initial routes with crawler
[nitro] ├─ /200.html (71ms)
[nitro] ├─ /404.html (72ms)
[nitro] ├─ / (117ms)
[nitro] ├─ /admin (35ms)
[nitro] ├─ /spa (34ms)
[nitro] ├─ /old-page (17ms)
[nitro] ├─ /moved (1ms)
[nitro] ├─ /isr (59ms)
[nitro] ├─ /lite (60ms)
[nitro] ├─ /ssr (61ms)
[nitro] ├─ /swr (64ms)
[nitro] ├─ /cached (63ms)
(_payload.json の行は省略)
[nitro] i Prerendered 18 routes in 1.741 seconds
[nitro] √ Generated public .output/public
[nitro] √ You can preview this build using npx serve .output/public
.output/public/200.html
.output/public/404.html
.output/public/_payload.json
.output/public/admin/index.html
.output/public/cached/index.html
.output/public/index.html
.output/public/isr/index.html
.output/public/lite/index.html
.output/public/moved/index.html
.output/public/old-page/index.html
.output/public/spa/index.html
.output/public/ssr/index.html
.output/public/swr/index.html
(各フォルダの _payload.json は省略)
nuxt generate ならではの変換が3つ見られました。
| 元のルール | generate後 | 注意点 |
|---|---|---|
redirect |
<meta http-equiv="refresh" content="0; url=/ssr"> だけのHTML |
301/307のステータスは返らない。恒久的な移転はホスティング側のリダイレクト設定で |
ssr: false |
中身が空の <div id="__nuxt"></div> のHTML |
SPAとしてブラウザで描画される |
swr / isr / なし |
ビルド時点の内容で固定されたHTML | ビルドし直すまで更新されない |
/200.html と /404.html は、静的ホスティングで SPAのフォールバックや404ページとして使うファイルです。
<!-- .output/public/moved/index.html -->
<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=/ssr"></head></html>
nuxt build と nuxt generate の使い分けは次のとおりです。
| コマンド | 出力 | 必要な環境 | routeRules |
|---|---|---|---|
nuxt build |
.output/server(Nitroサーバー)+ .output/public |
Node.js などのサーバー、またはサーバーレス | すべて有効 |
nuxt generate |
.output/public の静的ファイルのみ |
静的ホスティングだけでよい | redirect は meta refresh に、SWR・ISRは無効 |
デプロイ先ごとのISR・SWRの違い
同じ設定を Vercel向け(NITRO_PRESET=vercel)と Netlify向け(NITRO_PRESET=netlify)でビルドし、生成物を確認しました。実際のデプロイは行っていません(ビルド成果物とNitroのプリセットのコードで確認)。
| デプロイ先 | isr: 60 |
swr: 10 |
確認したこと |
|---|---|---|---|
Node.js(node-server) |
キャッシュなし(SSRと同じ) | Nitroがサーバー内でキャッシュ | 上の実測 |
| Vercel | ISR関数+{"expiration": 60} |
ISR関数+{"expiration": false}(期限なし) |
.vercel/output/functions/*.prerender-config.json |
| Netlify | Netlify-CDN-Cache-Control: public, max-age=60, stale-while-revalidate=31536000, durable を付与 |
Nitroの cache-control のまま |
Nitroのnetlifyプリセットのコード |
Vercel向けビルドで出たファイルと警告です。
.vercel/output/functions/isr-isr.func
.vercel/output/functions/isr-isr.prerender-config.json → { "expiration": 60 }
.vercel/output/functions/swr-isr.func
.vercel/output/functions/swr-isr.prerender-config.json → { "expiration": false }
[nitro] WARN Nitro now uses isr option to configure ISR behavior on Vercel. Backwards-compatible support for static and swr options within the Vercel Build Options API will be removed in the future versions. Set future.nativeSWR: true nitro config disable this warning.
Vercelで swr: 10 と書くと、互換処理により「期限なしのISR」に変換されます。 10秒ごとに更新したいつもりでも、そうならない点に注意が必要です。nitro: { future: { nativeSWR: true } } を設定して再ビルドすると、/swr は通常の関数(swr.func)になり、ISRへの変換はされませんでした。Vercelでは期限つきキャッシュは isr: 秒数 で書くのがわかりやすいです。
Netlify向けビルドでは、リダイレクトとヘッダーが dist/_redirects・dist/_headers に書き出されました。
# dist/_redirects
/moved /ssr 301
/old-page / 302
文字列だけの redirect(Node.jsでは307)は、Netlifyの _redirects では302として出力されていました。ステータスコードを揃えたい場合は { to, statusCode } で明示します。
ページ側で書く defineRouteRules(実験的機能)
nuxt.config.ts ではなくページのファイルにルールを書く方法もあります。現時点では実験的機能で、experimental.inlineRouteRules: true が必要です(この記事では実行していません。公式ドキュメント準拠)。
// nuxt.config.ts
export default defineNuxtConfig({
experimental: { inlineRouteRules: true },
})
<!-- app/pages/about.vue -->
<script setup lang="ts">
defineRouteRules({ prerender: true })
</script>
ページが増えるとルールが分散して見通しが悪くなるため、チーム開発では nuxt.config.ts にまとめる方が一覧性は高いです。
よくあるつまずきと対処
| 症状 | 原因 | 対処 |
|---|---|---|
isr を書いたのにNode.jsサーバーでキャッシュされない |
ISRはCDN(Vercel・Netlify)向けの機能 | Node.jsでは swr か cache を使う |
Vercelで swr のページが更新されない |
swr が期限なしISRに変換される |
isr: 秒数 で書く。または nitro.future.nativeSWR: true |
nuxt generate 後にリダイレクトが301にならない |
静的ファイルでは meta refresh になる | ホスティングのリダイレクト設定を使う |
ssr: false のページが検索結果に出ない |
HTMLに本文がない | 検索に載せたいページはSSRかprerenderに |
| prerenderしたページのデータが古い | ビルド時点で固定される | 再ビルドするか、swr・isr に変える |
curl -I /api/now が404になる |
now.get.ts はGET専用で、-I(HEAD)には一致しない |
curl -s -D - -o /dev/null でGETのヘッダーを見る |
| 動的ルートがprerenderされない | リンクがなくクローラーが見つけられない | nitro.prerender.routes か prerenderRoutes() で列挙 |
最後の curl -I の件は実測で遭遇しました。server/api/now.get.ts に curl -I を送ると 404 Page not found: /api/now でしたが、GETで取得すると 200 と正しいヘッダーが返りました。
よくある質問
Q. Nuxtの既定のレンダリング方式は何ですか?
A. SSR(ユニバーサルレンダリング)です。nuxt.config.ts で ssr: false を書くとアプリ全体がSPAになり、routeRules の ssr: false なら指定したパスだけがSPAになります。
Q. SWRとISRはどちらを使えばよいですか?
A. Node.jsサーバーやDockerで運用するならSWR、Vercel・NetlifyならISRが目安です。今回の実測では、Node.jsで isr: 60 を指定してもキャッシュされず、swr: 10 は約10秒ごとに更新されました。
Q. nuxt generate と prerender: true の違いは?
A. nuxt generate は全ページを静的化し、サーバーなしで配信します。prerender: true は指定したページだけを静的化し、残りはサーバーで動かすハイブリッド構成です。
Q. Next.jsのISRとの違いは?
A. 考え方は近く、Next.jsはページやfetch単位の revalidate、Nuxtは URLパターン単位の routeRules で指定します。Nuxtはキャッシュの実体がデプロイ先のプリセットで変わる点が特徴です。
まとめ
routeRulesでURLごとにSSR・SPA・SSG・SWR・ISRを選べる(ハイブリッドレンダリング)- 既定はSSR。更新が少ないページは
prerender: true、管理画面はssr: false swrはs-maxage、cacheはmax-ageを付与。SWRは期限切れ直後の1回だけ古い版を返し、裏で作り直す- Node.jsで
isrはキャッシュされない。Node.jsならswr/cache、Vercel・Netlifyならisr - Vercelで
swrは期限なしISRに変換されるため、期限つきはisr: 秒数で書く nuxt generateではリダイレクトが meta refresh になり、SWR・ISRは使えない- 設定したら
curlでヘッダーとHTMLの生成時刻を確かめると、意図どおりか一目でわかる
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Nuxt SSR・SSGの設定|routeRules全オプションとヘッダー実測」
株式会社RJCでは、フレームワークの設定を「書いて終わり」にせず、実際のヘッダーや出力で確かめる習慣をプロジェクトの中で仲間と共有しています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。