Nuxt で作ったコーポレートサイトや会員向けのWebアプリを保守していると、依存関係の更新で nuxt を 4.6 に上げるPRが届きます。たとえば、問い合わせフォームの送信を server/api/contact.post.ts で受け、会員ページのログイン状態を h3 のセッション Cookie で持っているサイトです(想定の場面)。気になるのは「4.6 に上げると何か壊れるか」と「Nuxt 5 に向けて、サーバー側のコードをいつ、どう書き換えるか」の2点です。
Nuxt 4.6.0 は 2026年10月5日(UTC。日本時間では6日)、4.6.1 は10月9日(同10日)に npm へ公開されました。4.6 には、サーバー側のコードを h3 や Nitro の版に依存せずに書くための読み込み先 nuxt/server が加わりました。編集部は 4.5.2 と 4.6.1 で同じ小さなアプリをビルドし、curl でリクエストを送って確かめました。

4.6 に上げるだけなら、既存のサーバーコードは動く
4.6 に上げただけで、既存のサーバーコードを書き換える必要はありません。編集部の環境でも、自動インポートの defineEventHandler・useSession・sendRedirect・getRouterParam(いずれも h3 v1 のもの)を使ったルートは、4.6.1 で書き換えずに動きました。
確認しておきたいのは Node.js の版です。nuxt の package.json の engines は、4.5.2 の ^22.19.0 || ^24.11.0 || >=26.0.0 から、4.6.0 では ^22.22.3 || ^24.15.0 || >=26.0.0 に上がっています。npm は既定では engines を警告として扱うだけでインストールは止まらないため、ビルドサーバー・本番サーバー・CI の Node.js の版を確かめます。版ごとの保守期限はNode.js 24は10月20日に保守期へで整理しています。
nuxt/server は Nuxt 5 に向けた書き方
nuxt/server の説明(docs の Server Imports)には、次のようにあります。
nuxt/serverships from Nuxt 4.6, so one file can serve Nuxt 4.6 runningnitropackv2, Nuxt 5 running Nitro v3, and anything a future builder brings.
アップグレードガイドによると、Nuxt 4 のサーバー側は Nitro v2 と h3 v1、開発中の Nuxt 5 は Nitro v3 と h3 v2 で動きます。nuxt/server から読み込んで書いたコードは、その差を Nuxt 側が吸収し、Nuxt 5 でも書き換えずに動く、という位置づけです。
getQuery・readBody・getCookie・sendRedirect・getRequestIP などのほか、Zod などのスキーマを受け取る readValidatedBody、Cookie にデータを封じるセッションの関数(useSession など)があります。ファイルのアップロードを読む readMultipartFormData や Nitro の useStorage・defineCachedEventHandler は含まれず、そうしたルートは h3 や nitropack/runtime から読み込んだまま、Nuxt 5 で見直す対象として残ります。
書き換えで止まりやすい点
ここからは、nuxt/server に書き換えたときに編集部の環境で起きたことです。
defineEventHandler も nuxt/server から読み込む
nuxt/server の関数は、Nuxt 4.6 では自動インポートされません。自動インポートされる同じ名前の関数は h3 v1 のものです。ヘルパーだけを nuxt/server から読み込み、defineEventHandler を自動インポートのままにすると、リクエストは500エラーになりました。
// server/api/mixed.post.ts(失敗する例)
import { readValidatedBody } from 'nuxt/server'
export default defineEventHandler(async (event) => { // ← h3 のもの
const body = await readValidatedBody(event, (b: any) => typeof b?.email === 'string')
return { ok: true, body }
})
POST /api/mixed → 500
[NUXT_E8012] `readValidatedBody` from `nuxt/server` was called with an h3 event.
The handler was defined with h3's `defineEventHandler` (auto-imported), which passes a different event.
import { defineEventHandler, readValidatedBody } from 'nuxt/server' と並べて読み込むと、正しい入力で200、email に @ を含まない入力で400(Validation failed)になりました。ビルドは通り、そのルートにリクエストが来て初めて失敗します。
4.5 系の環境ではビルドが通っても動かない
同じ nuxt/server のコードを Nuxt 4.5.2 でビルドすると、ビルドはエラーなく完了しました。出力には import ... from 'nuxt/server' がそのまま残り、起動してリクエストを送ると500エラーになります。
# Nuxt 4.5.2
GET /api/portable → 500
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './server' is not defined by "exports" in .../node_modules/nuxt/package.json
トップページは200のままなので、死活監視では気づけません。書き換えは、ロックファイルの nuxt が 4.6 以上であることを確かめてから行います。
sendRedirect は return する
アップグレードガイドの「Differences From h3 v1」の表には、nuxt/server の sendRedirect は応答を送らず、本文を返すので return sendRedirect(event, '/login') と書く、とあります。編集部の環境では、return を付けずに書くと、302 と location: /login は返る一方、本文は後に書いた {"reached":true} になりました。return を付けると、自動インポートの h3 の sendRedirect と同じ meta refresh の本文が返りました。ブラウザーは転送されるので気づきにくいものの、ハンドラーの戻り値がそのまま本文として送られます。sendRedirect( を検索し、return を付けて書き換えます。
URLの日本語は自動でデコードされない
getRouterParam は、nuxt/server では URL に現れたままの値を返します。日本語のスラッグや検索語をパスに含むサイトでは、ここで値が変わります。
GET /api/item/%E6%97%A5%E6%9C%AC%20a
h3(自動インポート) → "日本 a"
nuxt/server → "%E6%97%A5%E6%9C%AC%20a"
nuxt/server { decode: true } → "日本 a"
デコード後の値で照合しているコードには { decode: true } を付けます。ガイドの表には、ほかに createError の引数が { status, statusText } になる、$fetch の代わりに serverFetch を使う、といった違いも並んでいます。
IPアドレスは X-Forwarded-For を信頼しない
getRequestIP(event) は、既定では接続元のアドレスを返します。X-Forwarded-For: 203.0.113.9 を付けて送っても 127.0.0.1 が返り、{ xForwardedFor: true } を渡すと 203.0.113.9 が返りました。説明には「No forwarded header is trusted by default, because a client can send any value in one.」とあります。ロードバランサーの後ろで送信元IPによる連投の制限をしている場合は、オプションが要ります。ただし「Only do this when the proxy overwrites the header」とあり、利用者が送ったヘッダーを上書きするプロキシの場合に限ります。
セッションを使うなら NUXT_APP_SECRET を本番に設定する
4.6 では、アプリ全体の秘密の値 runtimeConfig.appSecret(環境変数 NUXT_APP_SECRET)が加わりました。nuxt/server の useSession は、password を渡さないとこの値から作った鍵で Cookie を封じます。ランタイム設定の説明には、開発時だけ値を自動で生成する、とあります。
In development, Nuxt generates a random secret and stores it in
.nuxt/app-secretonly whenappSecretis unset, so features that depend on it work without any setup. … Builds never generate one: setNUXT_APP_SECRETin every environment you deploy to.
編集部の環境での結果です。
nuxt devでは設定なしで{"visits":1}が返り、ログに[NUXT_B5028] A generated development appSecret is being used.の警告が出た- 本番ビルドを
NUXT_APP_SECRETなしで起動すると、/api/sessionは500(`appSecret` is not set. Set `NUXT_APP_SECRET` to at least 32 characters, or pass a secret explicitly.)。32文字未満の値でも同じ500 openssl rand -base64 32で作った44文字の値を設定すると200。set-cookie: nuxt-session=...; Path=/; HttpOnly; Secure; SameSite=Laxが返った
開発環境では動くのに本番だけ失敗するため、デプロイ先の環境変数に入れ忘れると、公開直後にログインや会員ページが500になります。
説明には「Use a different value for each environment, but keep it stable across deployments and server instances within that environment.」ともあります。編集部の環境でも、同じ値で起動した別のサーバーは発行済みの Cookie を読め、別の値のサーバーでは新しいセッションになりました(visits が1に戻った)。複数台では全台で同じ値を使い、値を変えるとセッションが切れることを運用手順に書いておきます。
もう1点、h3 の useSession と nuxt/server の useSession は別の実装です。Cookie の名前も h3 と nuxt-session で異なり、説明には「a session one of them issues will not unseal with the other」とあります。説明どおりなら、ログインのセッションを切り替えた時点で利用者は全員ログアウトした状態になります(編集部は相互の読み込みを試していません)。公開の時間帯を選び、必要なら告知します。
書き換えの進め方
以下は編集部の提案です。4.6 への更新と、nuxt/server への書き換えは分けて進めます。
- 4.6 への更新だけを先に出す。 Node.js の版(22.22.3 以上か 24.15.0 以上)を確認し、既存のサーバーコードはそのままにする
- 書き換えるルートを一覧にする。
readMultipartFormData・useStorageなどを使う、移せないルートを分けておく - 1ファイルずつ書き換える。
defineEventHandlerもnuxt/serverから読み込み、sendRedirect・getRouterParam・getRequestIPを見直す - セッションは最後に、告知して切り替える。 本番・ステージングそれぞれに
NUXT_APP_SECRETを設定してから公開する - 書き換えたルートすべてにリクエストを送って確かめる。 ビルドが通ることでは確認にならない
別のフレームワークのメジャー更新を案件で採用する判断は、Remix 3.0正式版 案件で採用する前に確かめる5つの点でも扱っています。
確認した範囲
2026年10月13日に、nuxt/nuxt の v4.6.0 タグの docs(Server Imports、Upgrade Guide、Runtime Config)と packages/nuxt/package.json、コミット履歴、registry.npmjs.org の公開日時を確認しました(資料調査)。あわせて Linux 上の Node.js 24.19.0 で、Nuxt 4.5.2 と 4.6.1(どちらも Nitro 2.13.4、h3 1.15.11)の同じアプリを
nuxt buildでビルドして起動し、curl でリクエストを送りました(実機検証)。逆プロキシ・HTTPS、node-server 以外のデプロイ先、Nuxt 5、handleCors・serverFetchは試していません。2026年10月16日に、nuxt.com の Nuxt 4.6 の告知と GitHub のリリースノート(v4.6.0)も確認し、本文の内容と食い違いがないことを確かめました。
Nuxt で作ったサイトの更新や、サーバー側のコードの保守は、開発・AI・自動化のご相談からお問い合わせください。
参考資料
- Nuxt Blog — Nuxt 4.6
- nuxt/nuxt — Release v4.6.0
- nuxt/nuxt — docs/3.guide/6.going-further/5.server-imports.md(v4.6.0)
- nuxt/nuxt — docs/1.getting-started/18.upgrade.md(v4.6.0、Moving to nuxt/server)
- nuxt/nuxt — docs/3.guide/6.going-further/10.runtime-config.md(v4.6.0、Application Secret)
- nuxt/nuxt — packages/nuxt/package.json(v4.6.0、engines)
- npm registry — nuxt(公開日時)









