こんにちは。最近、Docker上でNext.js(フロントエンド)とGo(バックエンド)を使ってアプリを開発しているのですが、画像表示に使っていた next/image で結構ハマったので、原因と解決方法をまとめておこうと思います。
環境構成
Next.js(フロント): ポート3000
Go(バックエンド): ポート8080
画像: Goサーバー側で /uploads に静的ファイルとして保存
Docker Compose: Next.js と Go をそれぞれ別サービスとして定義
フロントからは、http://localhost:8080/uploads/xxx.jpg のように画像を参照したい状況です。
発生したエラー
Next.js 側の コンポーネントで以下のように画像を表示しようとすると…
<Image
src="http://localhost:8080/uploads/sample.jpg"
width={500}
height={300}
alt="サンプル画像"
/>
ビルド時、もしくは表示時にこんなエラーが出ました:
Error: Invalid src prop (http://localhost:8080/uploads/xxx.jpg) on `next/image`, hostname "localhost" is not configured under images in your `next.config.js`
原因は next.config.js の images.domains(または remotePatterns)
next/image はセキュリティとパフォーマンスの理由から、外部ホストの画像を使う場合に 事前にホワイトリスト登録が必要です。
これを解決するため、next.config.ts に以下を追加しました:
images: {
remotePatterns: [
{
protocol: 'http',
hostname: 'localhost',
port: '8080',
pathname: '/uploads/**',
},
],
}
これで一旦はOK!…かと思いきや、Docker コンテナ内で Next.js を動かしたら画像が表示されない問題が発生しました。
Docker コンテナにおける localhost の罠
ここが最大のハマりポイントです。
Next.js を Docker コンテナで動かすと、localhost は「コンテナ自身」を指します。つまり:
http://localhost:8080 → frontend コンテナ内の8080ポートを見に行く
でも Go は別の backend コンテナで動いている
よって 接続できない(ECONNREFUSED エラー)
正しい解決方法:ホスト名は backend にする
Docker Compose では、サービス名が内部の DNS 名として解決されるので、
Next.js から Go にアクセスするには http://backend:8080 のように指定する必要があります。
例)next.config.ts
images: {
remotePatterns: [
{
protocol: 'http',
hostname: 'backend', // ←ここが重要
port: '8080',
pathname: '/uploads/**',
},
],
}
そして の src も
<Image
src="http://backend:8080/uploads/sample.jpg"
width={500}
height={300}
alt="サンプル画像"
/>
ローカルでも動かしたい? → 環境変数で切り替えよう
開発中(Dockerを使わず手元で動かすとき)には localhost でアクセスしたくなることもあります。
その場合は、環境ごとに hostname を切り替えられるようにすると便利です:
.env.local(ローカル開発用)
IMAGE_HOST=localhost
.env.docker(Docker用)
IMAGE_HOST=backend
next.config.ts
const IMAGE_HOST = process.env.IMAGE_HOST || 'localhost';
images: {
remotePatterns: [
{
protocol: 'http',
hostname: IMAGE_HOST,
port: '8080',
pathname: '/uploads/**',
},
],
}
まとめ
状況 | 使うホスト名 |
---|---|
ローカルで開発中(PC) | localhost |
Docker Compose 環境 | backend(サービス名) |
おわりに
普段何気なく使っている localhost が、Docker の中では全く違う意味を持つことにあらためて気づかされました。
同じような構成でハマっている人の助けになれば幸いです
※当記事は一部、ChatGPTを参考に執筆しています。