/ โ† Image Optimisation Fixes / Next.js Image Optimisation
In this guide
  1. What the Next.js image optimiser does
  2. How to tell whether optimisation is running
  3. The key configuration changes
  4. Self-hosting, Docker and the image library
  5. Platform notes
  6. A worked example
  7. The fix, step by step
  8. Edge cases worth knowing
  9. Keeping image optimisation working
  10. Questions and answers

What the Next.js image optimiser does

The next/image component does not serve your original file. It writes an <img> tag whose srcset points at a built-in endpoint, /_next/image, with the source address, a width and a quality in the query string. When a browser requests one of those addresses, the Next.js server fetches the original, resizes it, converts it to a modern format the browser accepts, caches the result and sends it back.

/_next/image?url=%2Fimages%2Fsofa.jpg&w=828&q=75

So optimisation depends on three things being in place: a running Next.js server or a hosting platform that provides the same endpoint, permission to fetch the source image, and an image library on the server to do the resizing. Break any one of them and you get broken images, errors in the logs, or full-size originals served to phones.

SymptomUsually means
Error that the hostname is not configuredRemote host not allowed in images.remotePatterns
Build fails with an image loader error on exportoutput: 'export' with the default loader
Images load but are the original size and formatunoptimized is set, or a custom loader ignores width
400 or 500 responses from /_next/imageBad source URL, upstream failure, or image library missing
Very slow images on every visitOptimised images are not being cached
Phones download desktop-sized filesMissing or wrong sizes prop

How to tell whether optimisation is running

Open the page in Chrome, open DevTools, choose the Network tab and filter by Img. Click an image request and read its address and response headers. An optimised image looks like this:

Request URL:   https://www.example.com/_next/image?url=%2Fimages%2Fsofa.jpg&w=828&q=75
Status:        200
content-type:  image/webp
cache-control: public, max-age=14400, must-revalidate
x-nextjs-cache: HIT

If the request goes straight to /images/sofa.jpg instead, the component is not optimising that image. If the content-type is still image/jpeg for a browser that accepts WebP, the format step is not happening. The x-nextjs-cache header shows whether the optimised file came from the cache: MISS on the first request and HIT afterwards is correct. MISS every time means the cache is not working.

You can test the endpoint directly from the command line, which removes the browser from the picture:

curl -sI "https://www.example.com/_next/image?url=%2Fimages%2Fsofa.jpg&w=828&q=75" \
  -H "Accept: image/avif,image/webp,*/*"

# compare with the original
curl -sI https://www.example.com/images/sofa.jpg | grep -i content-length

A 400 response with a short text body usually names the problem, such as a width that is not allowed or a URL parameter that is missing. A 500 or 502 points at the server side: the source could not be fetched, or the image library failed. Check the server logs at the same moment, because Next.js writes the underlying error there.

๐Ÿ’ก Check one image at each width the page asks for. A mobile layout may request w=640 while desktop requests w=1920, and a configuration change can break one and not the other.

The key configuration changes

Allow remote image hosts narrowly

Images from another domain, such as a headless CMS or a product feed, must be allowed in next.config.js. The older images.domains option is deprecated; use remotePatterns and keep each pattern as narrow as you can:

// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: 'images.example.com', pathname: '/products/**' },
      { protocol: 'https', hostname: 'cdn.examplecms.io', pathname: '/uploads/**' },
    ],
    formats: ['image/avif', 'image/webp'],
  },
};

A wildcard such as hostname: '**' makes the error go away, but it lets anyone use your server to resize images from any site, which costs you processing time and, on paid hosting, money. Restart the development server after changing the config, because it is only read on start.

Static export needs its own loader

With output: 'export' there is no Next.js server after the build, so the default optimiser cannot run and the build stops with an error saying the default loader is not compatible with export. You have two choices. Point a custom loader at an image CDN that resizes on request:

// next.config.js
module.exports = {
  output: 'export',
  images: { loader: 'custom', loaderFile: './image-loader.js' },
};

// image-loader.js
export default function imageLoader({ src, width, quality }) {
  return `https://images.example.com${src}?w=${width}&q=${quality || 75}&fm=webp`;
}

Or set images.unoptimized: true and resize images yourself at build time, keeping several widths of each file. The first choice keeps responsive images working; the second is fine for small sites with few images.

Describe the slot with sizes

Images that use fill or a responsive layout need a sizes prop. Without it the browser assumes the image is as wide as the screen and picks a large file. Describe the real width at each breakpoint:

<Image src="/images/sofa.jpg" alt="Grey three-seat sofa"
  fill sizes="(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw" />
โš ๏ธ Use priority only on the single main above-the-fold image. In Next.js 16 it is being replaced by a preload prop, so check the docs for your version. Marking many images this way removes lazy loading from all of them and slows the page.

Self-hosting, Docker and the image library

When you run Next.js yourself with next start or a standalone build, the optimiser uses the sharp package to resize and convert images. Recent versions of Next.js install it for you, but it is a native module with a separate binary per operating system and processor. That causes trouble in two common setups.

# inside the running container or server
node -e "require('sharp'); console.log('sharp ok')"
ls .next/standalone/node_modules | grep sharp

The optimised files are cached on disk in .next/cache/images. If that folder is not writable, which happens with read-only container file systems, or if it is wiped on each deploy, every request is resized again. Mount a writable volume for it, and put a CDN in front so most requests never reach Node at all. The minimumCacheTTL setting controls the shortest cache time; its default has changed between versions, so set it explicitly if you rely on it.

If the server is slow overall, image resizing makes it worse, because each first request for a size uses processor time. Our guide to high time to first byte covers the server side, and fixing image CDN setup covers handing the work to a dedicated service.

Platform notes

Where it runsWhat to check
VercelOptimisation is built in. Check your plan's image optimisation usage, because remote patterns that are too wide can use it up quickly.
NetlifyIts Next.js runtime routes /_next/image to its own image CDN. Remote hosts still need to be allowed in the config.
CloudflareAdapters for Workers do not run sharp. Use a custom loader pointing at Cloudflare Images or another resizing service.
Your own server behind nginxMake sure /_next/image is proxied to Node and that nginx is not caching error responses.
Static hosting, such as a Plesk site or S3Only output: 'export' works, so use a custom loader or unoptimized.

Middleware is a frequent hidden cause. If your middleware matcher runs on every path, it may redirect or rewrite requests to /_next/image, for example to add a locale prefix or to require a login. Exclude it in the matcher:

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

If you use a basePath, the endpoint moves under it, such as /shop/_next/image, so any proxy rules need to match that path. For wider Next.js performance work, see fixing images in Next.js and Core Web Vitals on Next.js.

A worked example

A small furniture shop moved its Next.js site from Vercel to a static host to save costs, with output: 'export' added to the config. The build failed with the loader error. A developer added images: { unoptimized: true } to get it building, and the site went live.

The site worked, but product listing pages now sent the original 2,400 pixel photos, around 900 KB each, to every phone. A category page with 24 products went from about 1.1 MB of images to over 20 MB, and mobile LCP rose from 2.1 to 5.8 seconds.

The fix was to remove unoptimized, add a custom loader file pointing at the shop's existing image CDN with the requested width and quality, and add a sizes prop to the product grid. No page components changed apart from the grid. After the rebuild, the Network panel showed WebP files of 30 to 60 KB at the widths each layout needed, and mobile LCP came back to 2.3 seconds.

The fix, step by step

Step 1

Confirm the symptom

Open a page in Chrome, filter the Network panel by Img and click an image request. If its address starts with /_next/image and the type is WebP or AVIF, optimisation is running for that image. If it points at the original file or returns an error status, note the exact status code and response body. Repeat for a remote image and a local image, because they fail for different reasons and need different fixes.

Step 2

Read the error message

Check the browser Console in development and the server logs in production at the moment the image fails. Next.js names the cause, such as a hostname that is not configured, an export that cannot use the default loader, or a failure in the image library. Copy the message exactly. Most fixes below match one of these messages, so this step tells you which one to apply first.

Step 3

Allow remote hosts

For images from another domain, add each host to images.remotePatterns in next.config.js with its protocol, hostname and the narrowest pathname that covers your images. Replace any old images.domains setting. Restart the server, because the config is only read on start, then reload the page and confirm the remote image now loads from /_next/image with a 200 status and a modern format.

Step 4

Fix static export

If the site uses output: 'export', set images.loader to custom and add a loaderFile that returns your image CDN address with the requested width and quality. If you have no CDN, set images.unoptimized to true and resize images yourself before the build. Rebuild, deploy and check in the Network panel that each image is requested at a sensible width rather than its full original size.

Step 5

Check the image library

On your own server or in Docker, run node -e "require('sharp')" where the app runs. If it fails, reinstall dependencies inside the container or server so the correct platform binary is installed, and for standalone builds confirm sharp exists in the output folder. Restart the app and request a /_next/image address with curl; a 200 response with a WebP type confirms the fix.

Step 6

Remove stray unoptimized

Search the codebase for unoptimized, both as a prop on images and in the config. Remove it wherever it was left in from testing, keeping it only for SVGs or cases you have chosen deliberately. Rebuild and check a few pages in the Network panel; images that were coming from their original address should now come from /_next/image at smaller sizes.

Step 7

Add sizes to responsive images

Find every image using fill or a width that changes with the layout, and add a sizes prop that describes its real width at each breakpoint, such as (min-width: 1024px) 25vw, 100vw. Reload on a phone-sized viewport and confirm the w value in the request matches the slot rather than the full screen width. Product grids and blog listings usually benefit most, because each card is much narrower than the screen.

Step 8

Fix caching

Make sure .next/cache/images is writable and survives between requests, and that /_next/image is excluded from middleware that redirects or rewrites. Put a CDN in front if you can. Request the same image twice with curl and read the x-nextjs-cache header: MISS then HIT shows the cache works. Repeated MISS responses mean the folder, the deploy process or the proxy still needs attention.

Step 9

Check priority and retest

Mark only the main above-the-fold image with priority, or preload in newer versions, and remove it from every other image. Run Lighthouse on mobile and compare LCP and total image weight with your starting figures. Note the results, then test one page per template, such as home, category and product, to confirm every layout now receives optimised images at the right widths.

Edge cases worth knowing

Keeping image optimisation working

For the wider picture on image weight and formats, see choosing the right image format and fixing oversized images.

Questions and answers

1. Why is Next.js image optimisation not working?

Next.js image optimisation usually fails for one of five reasons: a remote host is not allowed in the images config, the site uses static export with the default loader, the image library is missing on the server, an unoptimized setting is switched on, or middleware is interfering with the image endpoint. Check the Network panel to see whether images come from the image endpoint, then read the error in the Console or server logs, which usually names the cause directly.

2. How do I fix the hostname is not configured error?

Add the image's host to the remotePatterns list in your next.config.js file, with the protocol, hostname and path the images use, then restart the server. The error appears because Next.js only optimises remote images from hosts you have allowed, to stop others using your server to resize their images. Keep each pattern narrow, such as one hostname and one folder, rather than allowing any host, which can be abused and run up processing costs.

3. Can I use next/image with a static export?

Yes, you can use next/image with a static export, but not with the default loader, because there is no server after the build to resize images. Either set a custom loader that returns an address on an image CDN that resizes on request, or set unoptimized to true and prepare the image sizes yourself before the build. The custom loader keeps responsive images working without changing the components in your pages.

4. Do I need to install sharp for Next.js?

Recent versions of Next.js install sharp automatically, so most projects do not need to add it by hand. Problems appear when the installed binary does not match the server, for example when a lock file made on a Mac is used to build a Linux Docker image, or when a standalone build leaves the package out. Test it on the server with a one-line require command, and reinstall inside the container if it fails.

5. How can I tell if an image is being optimised?

An optimised image is requested from the image endpoint, with the original address, a width and a quality in the query string, and it usually comes back as WebP or AVIF. Open the Network panel in Chrome, filter by images and click a request to see its address and content type. If the request goes to the original file, or the type is still JPEG or PNG in a modern browser, that image is not being optimised.

6. Why are my Next.js images slow even when optimised?

Optimised images are slow when they are resized again on every request instead of being served from cache. Check the x-nextjs-cache response header: a MISS on every request means the cache folder is not writable, is cleared on each deploy, or is bypassed by a proxy. Mount a writable cache folder, put a CDN in front, and check the cache time setting. A missing sizes prop can also make phones download files far larger than needed.

7. Should I use the priority prop on all images?

No, use priority only on the single main image that appears above the fold, usually the hero or the first product photo. It switches off lazy loading and asks the browser to fetch the image early. On every image it removes the benefit entirely, because all images then compete for bandwidth at once and the important one arrives later. In Next.js 16 this prop is being replaced by preload, so check the documentation for your version.

Check whether your Next.js images are optimised

Our image optimisation audit checks every image on a page for size, format and dimensions, so you can see at once which ones are being served at full size.

Open Image Optimisation Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Image Optimisation Fixes  ·  How to Fix Images in Next.js  ·  How to Fix Page Speed in Next.js  ·  How to Fix Core Web Vitals in Next.js  ·  How to Fix Image CDN Setup  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.