Fix Next.js Image 400 Errors in Production
Why next/image requests return 400 Bad Request for remote images, how remotePatterns matching actually works, and how to tell an optimizer problem from an image-hosting problem.
On this page
next/image works locally and then returns a broken image with a 400 Bad Request after deployment. The image itself usually isn't the problem — the Image Optimization API's request to fetch and transform it is being rejected, and the reasons for that rejection are narrower than they first appear.
Two different requests, two different failure points
next/image doesn't just render an <img> tag pointed at your original URL. For anything not marked unoptimized, the browser requests a URL under /_next/image that Next.js's built-in optimizer handles server-side — fetching your original image, transforming it, and returning the result.
That means a broken image can fail at either of two independent points:
- The optimizer request (
/_next/image?url=...) — rejected by Next.js itself, before it even tries to fetch your image. - The original image URL — reachable in theory, but failing when the optimizer tries to fetch it (wrong content type, requires auth, DNS doesn't resolve from the server, etc.).
Test these separately. Open the original image URL directly in a browser tab, outside of next/image entirely. If that works but the page's image doesn't, the problem is in the optimizer step. If the original URL itself fails or requires authentication, no next/image configuration will fix that — the source needs to be fixed or made publicly fetchable first.
The most common cause: unconfigured remote hosts
By default, next/image will not optimize images from a host it hasn't been explicitly told to trust. Per the Next.js images configuration reference, you allow specific external sources with remotePatterns in next.config.js:
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "images.example.com",
port: "",
pathname: "/uploads/**",
},
],
},
};Any request whose protocol, hostname, port, or path doesn't match a configured pattern gets a 400 Bad Request from the optimizer — this is documented behavior, not an edge case: "Attempting to optimize any other path will respond with 400 Bad Request error." The same applies to hostname, protocol, and port mismatches for the object form of remotePatterns.
Common ways this mismatch happens:
- The image host works locally against one environment (a staging CDN, a local upload path) and a different host in production (a production CDN domain) — and only one of them is in
remotePatterns. - The pattern's
pathnameis narrower than the real image paths (/uploads/**configured, but images actually live under/media/**). - The
searchfield is omitted or set narrowly, and the real image URL carries a query string that doesn't match — Next.js notes that omittingsearchallows any query string, which is a security tradeoff worth making deliberately, not by accident. portis left empty (meaning "no port in the URL") when the actual image URL includes an explicit, non-default port.
Reading the failing request directly
Open DevTools → Network, find the request to /_next/image?url=...&w=...&q=..., and check:
- Status code.
400from the optimizer itself points atremotePatterns(or another optimizer-level rejection) — see the causes below. - Response body. Next.js's optimizer error responses describe why the request was rejected; read the actual text rather than assuming.
- The decoded
urlparameter. Copy it out and decode it to see exactly which host, path, and query stringnext/imageis trying to fetch — compare that literally against your configuredremotePatterns.
Environment differences: why "it works locally" doesn't transfer
A remotePatterns mismatch is often invisible in local development because local setups commonly point at a different image source than production — a local upload directory, a different bucket, or a staging CDN domain. The fix that resolves it locally doesn't fix production because the two environments are legitimately serving images from different hosts. Treat next.config.js's images configuration as environment-specific in your head, even if it's one file: confirm the hosts your production build actually serves images from, not just the ones your local .env points to.
Configuration changes require a restart or redeploy
next.config.js is read at build/start time, not per-request. A remotePatterns change:
- In local development, requires restarting
next dev— it will not take effect on a hot-reloaded page. - In production, requires a full rebuild and redeploy. Some hosting platforms cache the previous build's static assets and configuration behind a CDN layer; if a fix doesn't appear to take effect after deploying, check whether the platform needs an explicit cache purge or whether the deployment actually completed with the new
next.config.js, not a cached prior build.
Other causes of a 400 that aren't remotePatterns
remotePatterns is the most common cause, but not the only one:
- Incorrect content type. The optimizer inspects the fetched image's
Content-Type. A URL that returns HTML (an error page, a login redirect) instead of actual image bytes — often because the resource requires authentication the optimizer's server-side fetch doesn't have — will fail even if the host is correctly allowlisted. - Authentication-protected images. If the original image URL requires a cookie, signed token, or session that only the browser holds, the optimizer's server-side fetch (which doesn't carry the browser's session) will be denied by the origin server, independent of
remotePatterns. - Redirects from the image host. Next.js follows redirects from an already-allowed remote source without re-validating
remotePatternsagainst the redirect target, but excessive or unexpected redirect chains can still fail depending onmaximumRedirectsconfiguration. - Quality or format values outside the configured set. If
qualitiesorformatsare restricted inimagesconfig and a request asks for a value outside that set, the optimizer also returns400.
Do not assume remotePatterns is the fix for every next/image error — confirm which of these you're actually looking at from the response body before changing configuration.
Fixing it, step by step
- Copy the failing
/_next/imagerequest'surlparameter and decode it. - Open that decoded URL directly in a new browser tab, without going through
next/imageat all. - If it fails there too, fix the image source first — hosting, permissions, or content type — before touching
next.config.js. - If it loads fine directly, compare its protocol, hostname, port, and path against every entry in
remotePatterns. - Add or correct the matching pattern, being as specific as the real usage allows rather than defaulting to a broad wildcard.
- Restart (
next dev) or fully rebuild and redeploy (production) — a config-only save is not enough. - Reload the page and re-inspect the
/_next/imagerequest's status code.
Common mistakes
Using domains instead of remotePatterns
The domains config option is deprecated in favor of remotePatterns (see the Next.js images configuration reference) because it can't restrict protocol, port, or path — only hostname. A project still using domains for a new image source should migrate that entry to remotePatterns rather than adding to a deprecated list.
Widening remotePatterns more than necessary
Setting hostname to ** or omitting pathname/search allows the optimizer to fetch and transform images from a much broader set of URLs than intended, which Next.js's own documentation flags as not recommended for exactly this reason — it can let the optimizer be pointed at URLs you didn't intend to serve. Scope patterns to the actual host and path your images come from.
Testing only the optimizer URL, never the original
If the original image URL itself is broken, no remotePatterns change will fix it. Always test the two independently, as described above.
Forgetting a full redeploy after a config change
A local fix that "still doesn't work in production" is frequently just a deployment that hasn't picked up the new next.config.js yet, not a wrong fix.
Verification checklist
- The original image URL loads successfully in a browser tab, outside of
next/image. - The decoded
urlparameter from the failing/_next/imagerequest matches an entry inremotePatterns(protocol, hostname, port, and path). -
next.config.jswas rebuilt/redeployed after the configuration change, not just saved. - The
/_next/imagerequest now returns a successful status in the deployed environment. - No unnecessarily broad
remotePatternsentry (bare**hostname, unrestrictedsearch) was left in place after debugging.
Claims to manually verify before publishing
-
Claim: A specific
next/image400error you encountered matches the causes described here.- Why verification is needed: This article is written from Next.js documentation, not a captured incident with a specific image host or hosting provider.
- Suggested evidence: The sanitized failing request URL (with any tokens removed), its response body, and the
remotePatternsconfiguration before and after the fix.
-
Claim:
remotePatternsbehavior described here matches the Next.js version this project runs.- Why verification is needed:
remotePatternsgained thesearchfield and array-of-URLsupport in specific later versions; older versions behave differently. - Suggested evidence:
next --versionoutput and the matching version's images configuration documentation.
- Why verification is needed:
-
Claim: The production hosting platform in use caches builds or configuration in a way that delays a
remotePatternsfix from taking effect.- Why verification is needed: This is general caution, not a confirmed behavior of a named hosting provider for this project.
- Suggested evidence: The specific hosting provider's deployment/cache documentation and a sanitized before/after deploy timestamp showing when the fix actually took effect.
Related writing
- Server Actions in Next.js — boundaries, security contracts, and cache semanticsWhat crosses the network, how Actions relate to RPC ergonomics over POST, and the checklist for parity with disciplined API handlers.
- Deploy a Next.js Application on VercelImport a Next.js repository into Vercel, configure builds and environments, connect a domain, inspect logs, and verify production behavior.
- Set Up Google Analytics 4 and Search Console for Next.jsAdd consent-aware GA4 measurement and configure Search Console ownership, sitemaps, inspection, and indexing checks for a Next.js site.