How to Fix Next.js 'Invalid src prop' Error for External Image URLs (domains & remotePatterns Config)
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins
TL;DR
- What broke: Next.js
<Image>received ansrcfrom an external host not listed innext.config.js, throwingError: Invalid src propand hard-crashing the render. - How to fix it: Add the hostname to
remotePatterns(Next.js 13+) ordomains(legacy) inside theimageskey ofnext.config.js, then restart the dev server or redeploy. - Shortcut: Use our Client-Side Sandbox below to auto-refactor this — paste your failing
srcURL and currentnext.config.jsand get a corrected config instantly.
The Incident (What Does the Error Mean?)
The raw error thrown at runtime:
Error: Invalid src prop (https://cdn.example.com/images/hero.png) on `next/image`,
hostname "cdn.example.com" is not configured under images in your `next.config.js`.
See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host
Next.js's <Image> component does not proxy or render external images unless the origin hostname is explicitly whitelisted. The component fails at render time — not build time — meaning this hits production users first, not your CI pipeline. The page either white-screens or throws an unhandled error boundary depending on your setup.
The Attack Vector / Blast Radius
This is not just a DX annoyance. The whitelist exists to prevent Server-Side Request Forgery (SSRF) via Next.js's built-in image optimization API (/_next/image?url=...&w=...&q=...).
If you "fix" this by setting an overly broad wildcard — e.g., allowing ** as a hostname or using protocol: 'https', hostname: '**' — you've just turned your Next.js image optimization endpoint into an open proxy. An attacker can craft requests like:
GET /_next/image?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/&w=16&q=75
This hits the AWS EC2 Instance Metadata Service (IMDS). Your server fetches it, and depending on error handling, may leak credentials in logs or error responses. The blast radius: full IAM credential exfiltration from your compute environment.
Minimum-privilege hostname scoping is not optional.
How to Fix It (The Solution)
Basic Fix — Legacy domains Array (Next.js < 12.3, still works but deprecated)
// next.config.js
module.exports = {
images: {
- // no images config
+ domains: ['cdn.example.com'],
},
};
Do not use this for new projects. domains allows all protocols and all paths on that hostname. It's a blunt instrument.
Enterprise Best Practice — remotePatterns (Next.js 13+, required for 14+)
remotePatterns enforces protocol + hostname + port + pathname at the pattern level. Scope it as tightly as possible.
// next.config.js
module.exports = {
images: {
- domains: ['cdn.example.com'],
+ remotePatterns: [
+ {
+ protocol: 'https',
+ hostname: 'cdn.example.com',
+ port: '',
+ pathname: '/images/**',
+ },
+ ],
},
};
Key hardening decisions:
| Field | Insecure Value | Hardened Value |
|---|---|---||
| protocol | omitted (allows http) | 'https' explicit |
| hostname | '**.example.com' | 'cdn.example.com' exact |
| pathname | '/**' (all paths) | '/images/**' scoped |
After editing next.config.js, a full server restart is required — this is not hot-reloaded.
# Kill and restart
kill $(lsof -t -i:3000) && npm run dev
# or in production
pm2 restart next-app
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
This class of misconfiguration should never reach production. Here's how to gate it:
1. Checkov Static Analysis
Checkov has checks for Next.js misconfigs. Add to your pipeline:
# .github/workflows/security.yml
- name: Run Checkov on next.config.js
uses: bridgecrewio/checkov-action@master
with:
file: next.config.js
framework: javascript
2. Custom ESLint Rule
Write a lint rule that fails the build if remotePatterns contains hostname: '**' or if domains is non-empty in a Next.js 13+ project:
// eslint-plugin-local/no-wildcard-image-domains.js
module.exports = {
create(context) {
return {
Property(node) {
if (
node.key.name === 'hostname' &&
node.value.value === '**'
) {
context.report({
node,
message: 'Wildcard hostname in remotePatterns is an SSRF risk.',
});
}
},
};
},
};
3. OPA / Conftest Policy
If you manage next.config.js generation via infrastructure tooling:
# policy/next_image.rego
package nextjs.image
deny[msg] {
pattern := input.images.remotePatterns[_]
pattern.hostname == "**"
msg := "remotePatterns wildcard hostname is prohibited. Scope to exact CDN hostname."
}
deny[msg] {
count(input.images.domains) > 0
msg := "Deprecated 'domains' key detected. Migrate to remotePatterns with scoped pathname."
}
conftest test next.config.js --policy policy/
4. Dependency and Config Drift Alerts
Pin your Next.js version in package.json and use Renovate or Dependabot with a custom regex manager to detect if domains re-appears after a merge:
// renovate.json
{
"regexManagers": [{
"fileMatch": ["next\\.config\\.js"],
"matchStrings": ["domains:\\s*\\[(?<currentValue>[^\\]]+)\\]"],
"depNameTemplate": "next-image-domains-audit"
}]
}
The pattern: shift left. Catch this at lint → PR check → deploy gate. Never in production.