Skip to main content
ADR-016accepted

Scoped Remote Image Patterns over Generic CDN Proxy

Context

The portfolio displays images from multiple origins: profile photos and social cards from statics.lucioduran.com, dynamic content images from api.lucioduran.com, and the main domain lucioduran.com for statically hosted assets. Next.js Image component provides automatic optimization (WebP/AVIF conversion, responsive sizing, lazy loading) but requires explicit configuration of allowed remote image sources via remotePatterns in next.config.js. The configuration choice is between a permissive approach (allowing any hostname via a wildcard pattern) and a restrictive approach (explicitly listing each allowed origin). A wildcard pattern simplifies configuration but creates an open proxy — any external image URL processed through Next.js Image becomes an attack vector for SSRF (Server-Side Request Forgery), where an attacker could force the server to fetch internal network resources. For a publicly deployed site on Vercel, this could expose Vercel's internal infrastructure to probing.

Decision

Configure next.config.js remotePatterns with exactly three explicitly listed hostnames, all under the lucioduran.com domain: statics.lucioduran.com (static assets CDN), api.lucioduran.com (dynamic content images), and lucioduran.com (root domain assets). Each entry specifies protocol: 'https' — no HTTP sources allowed. No wildcard patterns, no catch-all rules, no third-party hostnames. Images from any other origin must be downloaded and self-hosted before they can be optimized through Next.js Image. This creates a closed system where every image URL processed by the optimization pipeline is from a controlled, first-party origin.

Consequences

Positive: The image optimization endpoint cannot be used as an SSRF proxy — only three known, controlled hostnames are processable. HTTPS-only enforcement prevents accidental mixed-content serving. The explicit listing serves as documentation of the site's image infrastructure — a new developer can immediately see which origins serve images. All three origins share a common root domain (lucioduran.com), making SSL certificate management consistent. The constraint forces discipline: every image must be hosted on owned infrastructure, preventing dependency on third-party image services that could change URLs or go offline. Negative: Adding a new image source (e.g., a profile photo from a third-party service, screenshots from external tools) requires a next.config.js change and redeploy. If a content entry references an image on an unlisted domain, the Next.js Image component falls back to an unoptimized img tag or errors — depending on configuration. The three-origin limitation means all images must be uploaded to owned infrastructure first, adding a manual step to the content authoring workflow.

Calibrated Uncertainty

Predictions at Decision Time

Expected the three-origin restriction to cover all current and near-future image sources. Predicted the manual upload step (images to owned infrastructure) would add negligible friction because image changes are infrequent. Assumed no third-party image sources would be needed.

Measured Outcomes

The three-origin restriction has covered 100% of image needs for 20+ months. No third-party image source has been required. The manual upload step is negligible as predicted — images change approximately once per quarter. The ~60% size reduction from WebP optimization is consistent across all image types (profile photos, social cards, content images). The unexpected benefit: having all images on owned infrastructure means zero risk of broken images from third-party URL changes or service discontinuations — a problem that has affected previous projects with images hotlinked from external services.

Unknowns at Decision Time

Did not know whether future content types would require images from external origins (e.g., GitHub avatar URLs, certification badge images from third-party issuers). So far they haven't — all images are self-hosted. Also unknown: whether Vercel's image optimization pricing would change for free tier users. Currently unlimited, but usage-based pricing could make aggressive image optimization costly at scale.

Reversibility Classification

Two-Way Door

Adding new origins to remotePatterns is a single line addition to next.config.js. Switching to a wildcard pattern (hostname: '**') is a one-line change. Removing Image optimization entirely and using standard img tags requires replacing Next.js Image imports across components. Estimated effort for any pattern change: 5 minutes plus redeploy.

Strongest Counter-Argument

The SSRF risk for a static portfolio site on Vercel is theoretical — there are no internal network resources to probe, and Vercel's infrastructure is not vulnerable to SSRF via image optimization endpoints. A wildcard pattern would simplify the workflow for adding new image sources and eliminate the manual upload step. For a single-developer project with full control over content, the security posture is arguably over-restrictive. The counter-counter: the restrictive pattern is not just about SSRF — it's about infrastructure ownership. Depending on external image hosts creates a silent availability risk that is harder to diagnose than a build failure.

Technical Context

Stack
Next.js Imagenext.config.js remotePatternsWebP/AVIFVercel Image Optimization
Allowed Origins
3
Protocol Restriction
HTTPS only
Wildcard Patterns
0
Avg Image Size Reduction
~60% (WebP vs PNG)
Constraints
  • No wildcard hostname patterns
  • HTTPS only
  • All origins must be first-party controlled

Related Decisions