Next.js のデプロイ先といえば Vercel が定番ですが、AWS Lambda へのデプロイを可能にする SST (Serverless Stack) という選択肢があります。
本記事では、SST で Next.js 16 をデプロイする方法と、PPR (Partial Pre-Rendering) を有効化する際にハマったポイントと対処法を記載します。
TL;DR
- SST は Next.js を AWS Lambda + CloudFront にデプロイできる
- Vercel に比べてコスト削減・AWS エコシステム統合がメリット
- Next.js 16 の PPR(
cacheComponents)を使う場合、Lambda ストリーミング設定が必須 - 設定を間違えるとすべてのリクエストが30秒タイムアウトしてしまう
目次
SST とは
SST (Serverless Stack) は、AWS 上にサーバーレスアプリケーションをデプロイするためのフレームワークです。
# SST でデプロイ
npx sst deploy --stage production
内部では以下の AWS サービスを自動構築します:
[CloudFront] → [Lambda Function URL] → [Next.js App]
↓
[S3] (静的アセット)
SST の特徴
SST 公式ドキュメントより:
- Infrastructure as Code: TypeScript で AWS リソースを定義
-
ローカル開発:
sst devでローカルと AWS を接続 - OpenNext 統合: Next.js を Lambda 向けに最適化ビルド
- 自動スケーリング: Lambda の特性を活かしたスケール
OpenNext について
SST は内部で OpenNext を使用しています。OpenNext は Next.js のビルド出力を AWS Lambda や Cloudflare Workers などの環境にデプロイ可能な形式に変換するフレームワークです。
SST で Next.js をデプロイする
1. SST のインストール
# 新規プロジェクト
npx create-sst@latest my-nextjs-app
cd my-nextjs-app
# 既存 Next.js プロジェクトに追加
npx sst@latest init
2. sst.config.ts の設定
// sst.config.ts
export default $config({
app(input) {
return {
name: "my-nextjs-app",
removal: input?.stage === "production" ? "retain" : "remove",
protect: ["production"].includes(input?.stage),
home: "aws",
providers: {
aws: {
region: "ap-northeast-1",
},
},
};
},
async run() {
const site = new sst.aws.Nextjs("Web", {
path: ".",
domain: {
name: "example.com",
aliases: ["www.example.com"],
},
warm: 5, // Lambda ウォームスタート
server: {
runtime: "nodejs22.x",
memory: "2048 MB",
timeout: "30 seconds",
},
});
return {
url: site.url,
};
},
});
3. デプロイ
# 開発環境
npx sst dev
# ステージング
npx sst deploy --stage staging
# 本番
npx sst deploy --stage production
これだけでドメインが AWS にあれば Next.js の環境が立ち上がります。
Next.js 16 と PPR の設定
PPR とは
PPR (Partial Pre-Rendering) は、ページの静的部分を事前レンダリングし、動的部分をストリーミングする機能です。
従来: [リクエスト] → [全体レンダリング] → [レスポンス]
↑
データ取得待ち(遅い)
PPR: [リクエスト] → [静的シェル即返却] → [動的部分ストリーミング]
↑ ↑
ビルド時生成(高速) Suspense でストリーム
PPR の有効化
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
// Next.js 15 以前
// ppr: true,
},
// Next.js 16 以降(ppr は cacheComponents に統合)
cacheComponents: true,
};
export default nextConfig;
レイアウト設計のポイント
PPR を活かすには、RootLayout を静的に保つことが重要です。
// app/layout.tsx - 静的(セッション取得しない)
export default function RootLayout({ children }) {
return (
<html>
<body>
<AppProviders session={null}>
{children}
</AppProviders>
</body>
</html>
);
}
認証が必要なページは、下層のレイアウトで connection() を使って動的オプトインします。
// app/(protected)/layout.tsx - 動的(connection()でオプトイン)
import { connection } from "next/server";
const ProtectedLayout = async ({ children }) => {
await connection(); // 動的レンダリングを明示
const session = await getServerAuthSession();
if (!session) redirect("/auth/sign-in");
return <>{children}</>;
};
なぜこの設計か?
- RootLayout でセッション取得すると、サイト全体が動的になる
- PPR は「静的シェル」を即座に返すことで高速化する
- RootLayout が動的だと、静的シェルをプリレンダリングできない
Lambda ストリーミングの落とし穴
症状:全リクエストが30秒タイムアウト
PPR を有効化して SST でデプロイしたら、以下の症状が発生しました:
- 全ページのナビゲーションが約30秒かかる
- CloudWatch Logs で全リクエストがタイムアウト直前で完了
- Vercel では正常に動作する
原因:Lambda の InvokeMode
Lambda Function URL には2つのモードがあります:
| モード | 動作 | Next.js Streaming |
|---|---|---|
BUFFERED |
レスポンス全体をバッファ | 動作しない |
RESPONSE_STREAM |
ストリーミング | 動作する |
SST はデフォルトで BUFFERED モードを使用します。
Next.js の Suspense/Streaming は RESPONSE_STREAM を必要とするため、BUFFERED ではレスポンスが完了せず、30秒タイムアウトまでハングします。
解決:open-next.config.ts を作成
SST は内部で OpenNext を使用します。ストリーミングは OpenNext の設定ファイルで有効化します。
// open-next.config.ts(プロジェクトルートに作成)
/**
* OpenNext configuration for AWS Lambda streaming
*
* CRITICAL: Without streaming enabled, Lambda uses BUFFERED mode which causes
* all requests to hang until 30s timeout because Next.js Suspense/Streaming
* never completes in buffered mode.
*/
const config = {
default: {
override: {
wrapper: "aws-lambda-streaming",
},
},
};
export default config;
設定確認
ビルド後、.open-next/open-next.output.json を確認:
{
"origins": {
"default": {
"streaming": true,
"wrapper": "aws-lambda-streaming"
}
}
}
デプロイ後、AWS CLI で確認:
aws lambda get-function-url-config --function-name <function-name>
# "InvokeMode": "RESPONSE_STREAM" を確認
トラブルシューティング
React Compiler でビルドエラー
Next.js 16 の React Compiler で一部ファイルがエラーになる場合、ファイル先頭でオプトアウトできます。
"use no memo";
export const ProblematicComponent = () => {
// ...
};
コールドスタートが遅い
// sst.config.ts
new sst.aws.Nextjs("Web", {
warm: 5, // プリウォーミング
server: {
memory: "2048 MB", // メモリ増加 = CPU増加
},
});
宣伝