LEARN · DEBUGGING GUIDE

Next.js Image Component Fails to Optimize Images

When Next.js <Image> silently skips optimization, page weight balloons and performance tanks. Here’s how to find the silent killers beyond just config typos.

IntermediateNext.js4 min read

What this usually means

Next.js skipped server-side image optimization, either serving source images or failing to proxy/transform them. This typically happens when the image domain isn’t whitelisted, the loader is misconfigured, required native dependencies (like sharp) aren’t available, or deployment environment disables optimization (e.g., static-only or incompatible hosting/CDN setup). Frequently, the failure is silent: the UI works, but images are huge and uncompressed, wrecking Core Web Vitals.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 1Open Chrome DevTools → Network → filter for 'image' and look for /_next/image requests (none = broken optimization).
  • 2Check next.config.js for images.domains array and confirm the image host is listed.
  • 3Inspect console output for warnings like: 'Image optimization is not compatible with next export'.
  • 4Run npm ls sharp in your repo root and verify sharp is installed.
  • 5If using a custom loader, set loader=default and observe if optimization resumes.
  • 6Hit an image route directly: /_next/image?url=%2Fimages%2Ffoo.jpg&w=640&q=75 and examine HTTP status/headers.
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • searchnext.config.js (images.domains, images.loader, images.deviceSizes)
  • searchpackage.json (dependencies: sharp, next)
  • searchDeployment logs for warnings about image optimization
  • searchNetwork tab for /_next/image?… traffic
  • search.next/server/chunks or .next/cache/images (image cache presence)
  • searchCloud/cdn config (Vercel, Netlify, custom CDN rules)
  • searchVercel/Netlify dashboard for build/runtime logs
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warningMissing or incomplete images.domains in next.config.js
  • warningUsing next export with Next.js <Image> component (static export disables optimization)
  • warningAbsent or failing sharp native module in production
  • warningCustom loader set incorrectly (e.g., loader='imgix' with no valid domain)
  • warningImage domain is HTTP, but config requires HTTPS
  • warningCDN misconfiguration bypasses or rewrites /_next/image
  • warningRunning behind a proxy that strips X-Forwarded-Host
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildExplicitly add every remote image domain to images.domains in next.config.js
  • buildRemove or update custom loader—test with the default loader first
  • buildInstall sharp: npm install sharp; rebuild and redeploy
  • buildSwitch deployment from static export (next export) to SSR/ISR (next start on Vercel/Node env)
  • buildReview CDN/proxy rules to ensure /_next/image routes pass to Next.js server
  • buildCheck all config values—typos in domain or protocol are frequent causes
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedNetwork panel shows /_next/image?… requests with 200 responses
  • verifiedCompare image payload sizes before/after fix—should drop >50%
  • verifiedFetch /_next/image?… directly and confirm 'Content-Type: image/webp' or 'Content-Type: image/avif'
  • verifiedLighthouse or WebPageTest audits report 'Properly sized images'
  • verifiedNo more console warnings/errors about image optimization
  • verifiedProduction logs show image optimization events (not just static serves)
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningBlindly adding custom loader when not needed—breaks built-in optimization
  • warningUsing next export with <Image /> and expecting optimization to work
  • warningAssuming local dev parity: Vercel and self-hosting differ in native dependencies
  • warningWhitelisting wildcard domains (e.g., '*')—Next.js ignores these
  • warningLeaving old CDN rules that bypass /_next/image after switching hosts
  • warningIgnoring silent failures: always check real network requests, not just UI
( 07 )War story

Production Launch: Unoptimized Hero Images in Next.js App

Frontend EngineerNext.js 12.3, Vercel, custom CDN, sharp 0.29.3

Timeline

  1. 09:30Deployment goes live on Vercel; hero images sourced from CDN image host
  2. 09:36Lighthouse alerts for 5MB initial payload
  3. 09:40Network panel shows images loading from https://cdn.example.com, not /_next/image
  4. 09:48Engineer inspects next.config.js: images.domains missing cdn.example.com
  5. 09:55Adds domain, redeploys; still no optimization
  6. 10:02Discovers CDN rewrites all /_next/* routes to index.html, breaking Next.js image proxy
  7. 10:15CDN bypass rule added for /_next/image; image optimization resumes

Our feature launch morning, I merged a PR replacing static images with Next.js <Image> and URLs from our CDN. On local, everything looked great—images crisp and fast.

Minutes after deploying to Vercel, our performance alert bot flagged 5MB page loads and the design team pinged us about 'blurry, large hero images.' Chrome’s network tab told me all images loaded directly from the CDN, not through the /_next/image proxy. Optimizations were missing entirely.

My first instinct was next.config.js, where I’d forgotten to add our CDN domain. Adding it fixed nothing. Only when I dug into our CDN’s config did I realize /_next/image was being rewritten away, preventing Next.js from touching any image requests. After a CDN rule change and redeploy, /_next/image worked, image payloads dropped 70%, and our Web Vitals recovered.

Root cause

Combination of missing domain in next.config.js and CDN path rewrite preventing /_next/image from reaching Next.js server.

The fix

Add CDN domain to images.domains and reconfigure CDN to pass /_next/image requests through.

The lesson

Always check network requests and CDN rules in production—Next.js image optimization is fragile to config drift.

( 08 )Why Next.js Skips Optimization Without Warning

Next.js fails silently when image optimization is impossible—falling back to raw <img> tags. Common triggers: unlisted remote domains, static export mode, or a failing sharp install. This leads to a production system serving multi-megabyte images without developer awareness unless network payloads are checked.

When deploying on incompatible hosts or custom CDNs, path rewriting can prevent /_next/image from ever reaching Next.js. Unlike SSR errors, there’s no stack trace—only a functional but unoptimized UI.

( 09 )Loader and Domain Gotchas

Setting a custom loader disables the built-in optimization. If loader=imgix, Next.js expects your domain to actually serve transformed images. Mistyping the loader or domain can render <Image> a no-op, leading to bloat.

Some engineers attempt to whitelist all origins with '*' or regex—Next.js ignores these for security. Each domain must be explicit, with correct scheme (http vs https) matching exactly what’s in the src.

( 10 )Native Dependencies: The Case of Missing sharp

Next.js uses sharp for image transformations. If sharp fails to install or isn't available (especially in custom self-host hosting or distroless Docker images), image optimization silently fails. Check production builds for logs like 'Could not load sharp'.

You can force Next.js to use a slower fallback (like Squoosh), but this is not recommended for production. Always check that sharp is in your node_modules and does not fail to load dynamically.

( 11 )CDN and Static Export Pitfalls

Deploying with next export generates static assets. <Image> requires SSR/ISR; with export, images just become <img> and lose optimization entirely. There is no warning in most logs.

CDNs like Cloudflare or custom NGINX rules often rewrite or proxy /_next/image, sometimes breaking Next.js routing or stripping required headers. Always check CDN behavior in production, not just staging.

Frequently asked questions

Why are my images not being served from /_next/image even after updating images.domains?

Check for CDN or proxy rules that rewrite or bypass the /_next/image path, and confirm your deployment isn't using next export or a custom loader.

Does Next.js <Image> work with static export?

No—image optimization requires SSR or ISR. With next export, <Image> outputs static <img> tags and skips all optimizations.

I set loader='default' but still see original images—what else should I check?

Verify sharp is present and builds successfully; also confirm images.domains is correct and matches the exact protocol of your image sources.

Can I whitelist all remote image domains with a wildcard?

No. Next.js requires each domain to be explicitly listed for security; wildcards are ignored.

What logs or signals show image optimization is working?

Network panel shows /_next/image?… requests with compressed images, and production logs may indicate sharp processing events. Payload sizes drop substantially.