Definition
When you can't use the framework's built-in image optimization, this is the intermediary server (endpoint) you build when you want the server to convert a large source image into the smaller size · lighter format the screen actually needs and serve that instead. The browser receives the image the server has resized · converted rather than the original.
More precisely, a server image proxy is an HTTP endpoint that fetches the upstream (the target server holding the original image) image, resizes · converts the format to match the display size the client requested (e.g., WebP, a lighter web image format), and responds. This changing of format · size is called transcoding. It layers a CDN edge cache (storing the result on a server node close to the user for reuse) and a function in-memory LRU (Least Recently Used, a memory cache that evicts the least recently used entries first), and on a miss (when the item isn't in cache and the original must be processed fresh) it reduces TTFB (Time To First Byte, the time from request until the first byte arrives) with a streaming tee (splitting one data stream into two — one responds immediately, the other saves to cache).
Why it matters
With the framework's images.unoptimized: true setting, <Image> may serve the original PNG/JPEG as-is. For images small on screen, like carousel thumbnails, simply converting them server-side to 336–672px WebP cuts transferred bytes by tens to hundreds of times, and the LCP (Largest Contentful Paint, the metric for when the largest content on screen is painted) bottleneck often shifts from "image download" to "render." In performance A/B testing, transferred bytes becomes a more reproducible headline (representative metric) than localhost LCP.
How it works
SSRF stands for Server-Side Request Forgery, a vulnerability where an attacker puts an internal-network address in the url parameter and makes the server request it on their behalf. An allowlist (a list of permitted hosts) blocks this.
| Layer | Role |
|---|---|
CDN (s-maxage) | durable after cold start, edge serving |
| module LRU | repeated hits on a warm lambda, ephemeral |
On the client, directly replacing src with the proxy URL is more deterministic in an unoptimized environment than using a loader. The <link rel="preload"> href must be the same URL to prevent a double download.
Practical application
- Allow only a fixed set of
wvalues (400 otherwise) — prevents DoS · cache key explosion. - Node runtime + sharp (or equivalent).
- MSW/E2E: if the mock URL can't reach the route, branch to bypass the proxy in the test environment.
Tradeoffs
| Choice | Benefit | Cost |
|---|---|---|
| own proxy | bypasses unoptimized, controls format · quality | server CPU · SSRF surface |
respond after toBuffer() | simple to implement | higher miss TTFB |
| streaming tee | fast first byte | needs cache-skip logic on mid-stream failure |
| WebP q60 | small bytes | quality · artifacts — tune per use case |
When not to use
- Adding a redundant proxy when a CDN · image CDN already provides optimization · caching.
- Fetching arbitrary URLs without an allowlist.
Common mistakes
- preload points at the original,
img srcat the proxy — preload wasted · double request. - Including the full presigned URL in the cache key — a miss on every signature rotation.
- Treating only localhost trace LCP as the headline — the real-deployment CDN gain is larger.
Related concepts
- ssrf-url-fetch-proxy-guard — security before fetch
- lqip-blur-placeholder-ssr — blur placeholder (separate from the LCP metric)
- resource-priority — preload · fetchpriority
- largest-contentful-paint — LCP bottleneck shift