Next.js アプリケーションをセルフホストする方法
Next.js アプリケーションをデプロイする際、インフラストラクチャに応じてさまざまな機能の処理方法を設定したい場合があります。
🎥 視聴: Next.js のセルフホスティングについてさらに詳しく → YouTube (45分)。
画像最適化
画像最適化は next/image を使用して、next start でデプロイする場合、ゼロコンフィグレーションでセルフホストで動作します。画像を最適化する別のサービスが必要な場合は、画像ローダーを設定できます。
画像最適化は、next.config.js でカスタム画像ローダーを定義することにより、静的エクスポートとともに使用できます。画像はビルド時ではなく、実行時に最適化されることに注意してください。
知っておくと良いこと
プロキシ
Proxy は next start でデプロイする場合、ゼロコンフィグレーションでセルフホストで動作します。着信リクエストへのアクセスが必要なため、静的エクスポートではサポートされていません。
Proxy は、低レイテンシーを保証するために、アプリケーションのすべてのルートまたはアセットの前に実行される可能性がある Node.js API のサブセットであるEdge ランタイムを使用します。これが必要ない場合は、Proxy を実行するために完全な Node.js ランタイムを使用できます。
すべての Node.js API を必要とするロジックを追加したり(または外部パッケージを使用したり)したい場合は、このロジックをレイアウトのサーバーコンポーネントに移動できる場合があります。たとえば、ヘッダーをチェックしてリダイレクトするなどです。ヘッダー、Cookie、またはクエリパラメータを使用して、next.config.js でリダイレクトまたは書き換えすることもできます。それでもうまくいかない場合は、カスタムサーバーを使用することもできます。
環境変数
Next.js は、ビルド時と実行時の両方の環境変数をサポートできます。
デフォルトでは、環境変数はサーバーでのみ利用可能です。ブラウザに環境変数を公開するには、NEXT_PUBLIC_ でプレフィックスを付ける必要があります。ただし、これらの公開環境変数は、next build 中に JavaScript バンドルにインライン化されます。
動的レンダリング中にサーバーで環境変数を安全に読み取ることができます。
import { connection } from 'next/server'
export default async function Component() {
await connection()
// cookies, headers, and other Dynamic APIs
// will also opt into dynamic rendering, meaning
// this env variable is evaluated at runtime
const value = process.env.MY_VALUE
// ...
}これにより、複数の環境を異なる値で昇格できる単一の Docker イメージを使用できます。
知っておくと良いこと
register関数を使用して、サーバー起動時にコードを実行できます。
キャッシュとISR
Next.js は、レスポンス、生成された静的ページ、ビルド出力、および画像、フォント、スクリプトなどのその他の静的アセットをキャッシュできます。
ページ(Incremental Static Regeneration を使用)のキャッシュと再検証には、同じ共有キャッシュが使用されます。デフォルトでは、このキャッシュは Next.js サーバーのファイルシステム(ディスク)に保存されます。これは、Pages Router と App Router の両方を使用してセルフホスティングする場合に自動的に機能します。
キャッシュされたページやデータを耐久性のあるストレージに永続化したい場合、または Next.js アプリケーションの複数のコンテナやインスタンス間でキャッシュを共有したい場合に、Next.js のキャッシュ設定を構成できます。
自動キャッシュ
- Next.js は、真に不変なアセットに対して
Cache-Controlヘッダーをpublic, max-age=31536000, immutableに設定します。これは上書きできません。これらの不変ファイルにはファイル名に SHA-hash が含まれているため、無期限に安全にキャッシュできます。例: ローカル画像。画像については、TTL を設定できます。 - Incremental Static Regeneration (ISR) は
Cache-Controlヘッダーをs-maxage: <getStaticProps で再検証する時間>, stale-while-revalidateに設定します。この再検証時間は、秒単位でgetStaticProps関数で定義されます。revalidate: falseを設定すると、デフォルトで 1 年間のキャッシュ期間になります。 - 動的にレンダリングされたページは、ユーザー固有のデータがキャッシュされないように、
Cache-Controlヘッダーをprivate, no-cache, no-store, max-age=0, must-revalidateに設定します。これは App Router と Pages Router の両方に適用されます。これには Draft Mode も含まれます。
静的アセット
静的アセットを別のドメインまたは CDN でホストしたい場合は、next.config.js の assetPrefix 設定を使用できます。Next.js は JavaScript または CSS ファイルを取得する際にこのアセットプレフィックスを使用します。アセットを別のドメインに分離すると、DNS および TLS の解決に余分な時間がかかるという欠点があります。
キャッシュの設定
デフォルトでは、生成されたキャッシュアセットはメモリ(デフォルト 50mb)とディスクに保存されます。Kubernetes のようなコンテナオーケストレーションプラットフォームで Next.js をホスティングしている場合、各ポッドはキャッシュのコピーを持ちます。デフォルトでキャッシュがポッド間で共有されないため、古いデータが表示されるのを防ぐには、Next.js キャッシュを構成してキャッシュハンドラーを提供し、インメモリ キャッシュを無効にすることができます。
セルフホスティング時に ISR/データキャッシュの場所を構成するには、next.config.js ファイルでカスタムハンドラーを構成できます。
module.exports = {
cacheHandler: require.resolve('./cache-handler.js'),
cacheMaxMemorySize: 0, // disable default in-memory caching
}次に、プロジェクトのルートに cache-handler.js を作成します。例:
const cache = new Map()
module.exports = class CacheHandler {
constructor(options) {
this.options = options
}
async get(key) {
// This could be stored anywhere, like durable storage
return cache.get(key)
}
async set(key, data, ctx) {
// This could be stored anywhere, like durable storage
cache.set(key, {
value: data,
lastModified: Date.now(),
tags: ctx.tags,
})
}
async revalidateTag(tags) {
// tags is either a string or an array of strings
tags = [tags].flat()
// Iterate over all entries in the cache
for (let [key, value] of cache) {
// If the value's tags include the specified tag, delete this entry
if (value.tags.some((tag) => tags.includes(tag))) {
cache.delete(key)
}
}
}
// If you want to have temporary in memory cache for a single request that is reset
// before the next request you can leverage this method
resetRequestCache() {}
}カスタムキャッシュハンドラーを使用すると、Next.js アプリケーションをホストするすべてのポッドで一貫性を確保できます。たとえば、キャッシュされた値を Redis や AWS S3 など、どこにでも保存できます。
知っておくと良いこと
revalidatePathは、キャッシュタグの便利なレイヤーです。revalidatePathを呼び出すと、指定されたページの特別なデフォルトタグでrevalidateTag関数が呼び出されます。
ビルドキャッシュ
Next.js は next build 中に ID を生成し、提供しているアプリケーションのバージョンを識別します。同じビルドを使用して複数のコンテナを起動する必要があります。
各環境ステージで再ビルドする場合、コンテナ間で一貫したビルド ID を生成する必要があります。next.config.js で generateBuildId コマンドを使用します。
module.exports = {
generateBuildId: async () => {
// This could be anything, using the latest git hash
return process.env.GIT_HASH
},
}バージョン不整合
Next.js は、バージョン不整合 のほとんどのインスタンスを自動的に緩和し、検出時に新しいアセットを取得するためにアプリケーションを自動的にリロードします。たとえば、deploymentId に不一致がある場合、ページ間の遷移はプリフェッチされた値を使用するのではなく、ハードナビゲーションを実行します。
アプリケーションがリロードされると、ページナビゲーション間で状態が永続化するように設計されていない場合、アプリケーションの状態が失われる可能性があります。たとえば、URL の状態やローカルストレージを使用すると、ページリフレッシュ後に状態が永続化されます。ただし、useState のようなコンポーネントの状態は、このようなナビゲーションで失われます。
ストリーミングと Suspense
Next.js App Router は、セルフホスティング時にストリーミングレスポンスをサポートしています。Nginx または同様のプロキシを使用している場合は、ストリーミングを有効にするためにバッファリングを無効にするように構成する必要があります。
たとえば、Nginx で X-Accel-Buffering を no に設定してバッファリングを無効にできます。
module.exports = {
async headers() {
return [
{
source: '/:path*{/}?',
headers: [
{
key: 'X-Accel-Buffering',
value: 'no',
},
],
},
]
},
}キャッシュコンポーネント
Cache Components は Next.js でデフォルトで動作し、CDN 専用の機能ではありません。これには、Node.js サーバー(next start 経由)としてのデプロイメント、および Docker コンテナとの使用が含まれます。
CDN との連携
Next.js アプリケーションの前に CDN を使用する場合、動的 API にアクセスすると、ページには Cache-Control: private レスポンスヘッダーが含まれます。これにより、結果の HTML ページがキャッシュ不能としてマークされます。ページが完全に静的にプリレンダリングされる場合、Cache-Control: public が含まれ、CDN でページをキャッシュできるようになります。
静的コンポーネントと動的コンポーネントの混在が必要ない場合は、ルート全体を静的にして、CDN で出力 HTML をキャッシュできます。これは、動的 API が使用されていない場合、next build を実行する際のデフォルトの動作である自動静的最適化です。
Partial Prerendering が安定版に移行するにつれて、Deployment Adapters API を通じてサポートを提供します。
after
after は、next start でセルフホスティングする場合に完全にサポートされています。
サーバーを停止する際は、SIGINT または SIGTERM シグナルを送信し、待機することで、正常なシャットダウンを保証してください。これにより、Next.js サーバーは、after 内で使用される保留中のコールバック関数またはプロミスが完了するまで待機できます。
役に立ちましたか?