Images.weserv.nl
Self-hostable image proxy and on-the-fly resizing service
Images.weserv.nl is the open-source software behind the wsrv.nl image CDN, providing on-the-fly image resizing, cropping, and format conversion. It can be self-hosted as an image processing and caching proxy for photo collections.
Key features
- On-the-fly image resizing
- Format conversion
- Image proxying
- Caching support
Pros & cons
Strengths
- Battle-tested at scale
- Flexible URL API
Trade-offs
- Processing service only
- Nginx module setup
Images.weserv.nl replaces
Last reviewed Sep 13, 2026 · 845 words
Images.weserv.nl is not a photo manager, and it is filed under photos only because the word "image" is in the name. It is the C++ nginx module that powers the public wsrv.nl image CDN: give it a URL and some parameters, and it fetches the picture, resizes, crops, converts it to WebP or AVIF, and caches the result. Self-hosting it makes sense for exactly one kind of person: someone serving a website or app that needs on-the-fly thumbnails and format conversion, who wants a proxy that has been hammered at public-CDN scale for over a decade. If you want to browse your holiday photos, close this tab and open Immich.
Where it sits in a stack
Think of it as a sidecar between your origin images and your visitors. Your CMS, gallery or app stores full-size originals somewhere reachable over HTTP; your pages reference https://img.example.com/?url=origin.example.com/photo.jpg&w=800&output=webp; the proxy fetches the original once, produces the variant, and nginx caches it. Nothing is stored except the cache. That makes it stateless, trivial to run in several replicas, and useless without an origin.
The design decision that shapes everything is that it only fetches over HTTP. There is no local filesystem mode. If your originals live on disk, put a static file server in front of them, or pick a different tool (imgproxy and Thumbor both read local paths and S3 directly).
Running the container
The project publishes an image, and it needs a larger-than-default shared memory allocation because libvips does its work in memory:
docker run -d --name weserv --shm-size=1gb -p 8080:80 ghcr.io/weserv/images:5.x
Then curl "http://localhost:8080/?url=example.com/image.jpg&w=300" returns a 300-pixel-wide version. Memory is what you pay for: the catalogue's 256 MB minimum is realistic for small images, but a single 40-megapixel source can push a worker well past that during processing, so size RAM for your largest originals rather than your average. The API documentation is the reference for everything below.
Building from source is a real undertaking: it needs libvips with the right codecs, nginx headers and a C++ toolchain, and the catalogue's "nginx module setup" con is the polite version of that. Use the container.
The URL parameters you will actually use
Width w and height h, with fit=cover to crop to those dimensions or fit=contain to letterbox; dpr=2 for retina variants; q=80 for quality; output=webp or output=avif for format conversion; we to prevent enlargement of small originals; blur, sharp and mask=circle for effects; trim to remove uniform borders. Add &output=json to get the source image's dimensions and metadata without an image. Everything is a GET parameter, so a template engine can generate variants without any SDK, which is the "flexible URL API" the catalogue credits it with.
Cache it and lock it down
Two things before it faces the internet. First, cache: nginx proxy_cache in front of the module, keyed on the full URL, with a long inactive time, turns a CPU-bound resize into a disk read for every visitor after the first. Second, restrict the url parameter to your own hostnames at the nginx layer. As shipped, the service will fetch and transform any public URL anyone asks for, which is what a public CDN wants and what a private deployment absolutely does not: you would be running an open image proxy that hides someone else's hotlinking behind your bandwidth bill. The public wsrv.nl instance exists precisely so that people who want that convenience do not need to host it.
imgproxy is the more common self-hosted pick
For a self-hoster the honest comparison is with imgproxy: Go, one binary, reads local files and S3 as well as URLs, signs every URL with an HMAC so it cannot be abused, and is what most self-hosted apps that offload image processing already integrate with. Thumbor is the older Python option with a plugin ecosystem. Images.weserv.nl wins when you want nginx-native caching, the largest set of URL operations, and the confidence of code that has served a public CDN since 2014. It loses on filesystem access and on secure-by-default URLs. If the images you are serving are your own site's assets, imgproxy is the safer and simpler pick; the photos category separates these processing tools from the galleries people usually mean.
What I'd do
Run the container only if you already have an HTTP origin for your originals and a template layer that can build URLs; put nginx caching and an allowed-hosts check in front before opening it up. For a personal site or a homelab gallery, deploy imgproxy instead. For managing photographs rather than serving them, this was never the right tool, and Immich is.
Compare Images.weserv.nl
1 head-to-head comparisons.
Similar photo management apps
Immich
Photo ManagementHigh-performance self-hosted photo and video backup
Replaces Google Photos, iCloud Photos
PhotoPrism
Photo ManagementAI-powered photo app for the decentralized web
Replaces Google Photos
Ente Photos
Photo ManagementEnd-to-end encrypted photo storage and backup
Replaces Google Photos, iCloud Photos
DeepFace
Photo ManagementFace recognition and facial attribute analysis framework
Replaces AWS Rekognition, Azure Face API
gallery-dl
Photo ManagementCommand-line tool to download image galleries from websites
Replaces Manual downloads
Color Thief
Photo ManagementLibrary to extract color palettes from photos
Replaces Commercial color analysis APIs