
Next.js v15.4.5以降、next/imageの最適化でAPI RoutesにヘッダーとCookieが渡らない問題と解決策
ありえないくらいニッチなネタですが、Next 15.4.4 から Next 15.4.5 にあげた際に Next/Image の最適化した際に挙動が異なります。最適化した際にはヘッダーとクッキーが URL に伝播しなくなります。
挙動の確認
API Routes を下記のように実装します。headers と cookies を出力して、Canvas で画像を返すだけです。
import { createCanvas } from 'canvas';
import { cookies } from "next/headers";
import { type NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
console.log('headers:', request.headers)
console.log('cookies:', await cookies())
const canvas = createCanvas(200, 200);
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 200, 200);
ctx.fillStyle = 'white';
ctx.font = '24px Arial';
ctx.fillText('Hello, World!', 35, 100);
const buffer = canvas.toBuffer('image/jpeg')
return new NextResponse(new Uint8Array(buffer), {
headers: {
'Content-Type': 'image/jpeg',
},
})
}
そして API Routes から画像を取得して表示する Page を作成します。
import Image from "next/image";
export default function Home() {
return (
<div>
<Image src="/api/r" width={100} height={100} alt=""/>
</div>
);
}
この時に、Next.js 15.4.4 まではヘッダーとクッキーが取得できます。
headers: Headers {
host: 'localhost:3000',
connection: 'keep-alive',
pragma: 'no-cache',
'cache-control': 'no-cache',
'sec-ch-ua-platform': '"Android"',
'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Mobile Safari/537.36',
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
'sec-ch-ua-mobile': '?1',
ただし、 Next. 15.4.5 からは取得できなくなります。ヘッダーの情報がだいぶ減りましたね...。
headers: Headers {
'x-forwarded-host': 'localhost',
'x-forwarded-port': '3000',
'x-forwarded-proto': 'http',
'x-forwarded-for': '::1'
}
cookies: ResponseCookies { _parsed: Map(0) {}, _headers: Headers {} }
挙動の理由と修正
_next/image
から始まるパスの場合にヘッダーが伝播されなくなりました。
Changelog 的には「fix(next/image): fix image-optimizer.ts headers」と書いています。
内容をよく見ると headers が渡されないようになっていることがわかります。
この変更は /next/image
から始まる場合に限ります。直接 /api/r
などのようにアクセスすると当然ヘッダーとクッキーは伝播されます。
つまり、最適化オプションを無効にすることで解決します。
<Image src="/api/r" width={100} height={100} alt="" unoptimized/>
HTML 構造自体はバージョンの差分で変化していません。
img 要素に src を直接の URL として渡すことで解決しているのです。
//
<!-- optimized -->
<img alt="" loading="lazy" width="100" height="100" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2Fapi%2Fr&w=128&q=75 1x, /_next/image?url=%2Fapi%2Fr&w=256&q=75 2x" src="/_next/image?url=%2Fapi%2Fr&w=256&q=75">
<!-- unoptimized -->
<img alt="" loading="lazy" width="100" height="100" decoding="async" data-nimg="1" style="color:transparent" src="/api/r">
...
さいごに
冒頭で書いたように、非常にニッチで、99% の人にとって無縁な内容だと思います。ただ、幸運なことに私は 1% 側の人間で、さらに良いことに解決できたので他の人に役立てば嬉しいのでブログにしました。