ImgProxy is an open-source image-processing server — resize, convert, and transform images on the fly via URL parameters, ideal as a caching layer in front of a CDN or web app. This guide builds ImgProxy from source on Ubuntu, runs it as a systemd service behind Nginx with TLS, walks through its URL processing options, and secures it with signed URLs.
Prerequisites: an Ubuntu server, a domain A record (e.g.
imgproxy.example.com), non-root sudo user.
Install ImgProxy
ImgProxy uses libvips for image processing; this builds it from source with Go.
$ sudo add-apt-repository ppa:dhor/myway
$ sudo apt update
$ sudo apt install libvips-dev -y
$ sudo snap install --classic --channel=latest/stable go
$ git clone https://github.com/imgproxy/imgproxy.git
$ cd imgproxy
$ sudo CGO_LDFLAGS_ALLOW="-s|-w" go build -o /usr/local/bin/imgproxy
Enter fullscreen mode Exit fullscreen mode
Create the environment config:
$ sudo touch /usr/local/bin/imgproxy.env
$ sudo nano /usr/local/bin/imgproxy.env
Enter fullscreen mode Exit fullscreen mode
IMGPROXY_BIND=:8080
IMGPROXY_NETWORK=tcp
IMGPROXY_READ_TIMEOUT=10
IMGPROXY_WRITE_TIMEOUT=10
IMGPROXY_WORKERS=2
IMGPROXY_REQUESTS_QUEUE_SIZE=0
IMGPROXY_QUALITY=100
IMGPROXY_PREFERRED_FORMATS=webp,jpeg,png,gif,avif
IMGPROXY_LOG_FORMAT="pretty"
IMGPROXY_LOG_LEVEL="INFO"
IMGPROXY_WATERMARK_URL=https://example.com/watermark.png
IMGPROXY_WATERMARK_OPACITY=1
Enter fullscreen mode Exit fullscreen mode
Key settings: IMGPROXY_WORKERS should be ~2× your vCPU count; IMGPROXY_REQUESTS_QUEUE_SIZE=0 means unlimited queueing; IMGPROXY_WATERMARK_URL points at whatever image you want overlaid when watermarking is enabled.
Point ImgProxy at the config and test:
$ export IMGPROXY_ENV_LOCAL_FILE_PATH=/usr/local/bin/imgproxy.env
$ cd
$ imgproxy
Enter fullscreen mode Exit fullscreen mode
WARNING [2024-05-28T00:40:42Z] No keys defined, so signature checking is disabled
WARNING [2024-05-28T00:40:42Z] No salts defined, so signature checking is disabled
INFO [2024-05-28T00:40:42Z] Starting server at :8080
Enter fullscreen mode Exit fullscreen mode
Stop it with Ctrl+C once verified, then set it up as a service.
Run ImgProxy as a systemd Service
$ sudo useradd -r -s /bin/false imgproxy
$ sudo chown imgproxy:imgproxy /usr/local/bin/imgproxy
$ sudo chown imgproxy:imgproxy /usr/local/bin/imgproxy.env
$ sudo nano /etc/systemd/system/imgproxy.service
Enter fullscreen mode Exit fullscreen mode
[Unit]
Description=IMGProxy Image Processing Application
After=network.target
[Service]
Environment="IMGPROXY_ENV_LOCAL_FILE_PATH=/usr/local/bin/imgproxy.env"
ExecStart=/usr/local/bin/imgproxy
User=imgproxy
Group=imgproxy
Restart=always
StandardOutput=append:/var/log/imgproxy/access.log
[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode
$ sudo mkdir -p /var/log/imgproxy/
$ sudo chown -R imgproxy:imgproxy /var/log/imgproxy
$ sudo systemctl daemon-reload
$ sudo systemctl enable imgproxy
$ sudo systemctl start imgproxy
$ sudo systemctl status imgproxy
Enter fullscreen mode Exit fullscreen mode
Put Nginx in Front
$ sudo apt install nginx -y
$ sudo nano /etc/nginx/sites-available/imgproxy.example.com
Enter fullscreen mode Exit fullscreen mode
server {
listen 80;
server_name imgproxy.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
Enter fullscreen mode Exit fullscreen mode
$ sudo ln -s /etc/nginx/sites-available/imgproxy.example.com /etc/nginx/sites-enabled/
$ sudo nginx -t
$ sudo systemctl restart nginx
$ sudo ufw allow 80 comment 'ImgProxy Http'
$ sudo ufw reload
Enter fullscreen mode Exit fullscreen mode
TLS:
$ sudo apt install python3-certbot-nginx -y
$ sudo certbot --nginx -d imgproxy.example.com --agree-tos
$ sudo systemctl restart nginx
$ sudo ufw allow 443 comment 'ImgProxy Https'
$ sudo ufw reload
Enter fullscreen mode Exit fullscreen mode
Process Images via URL
ImgProxy's URL format is https://imgproxy-url/[processing-options]/source-image-url, supporting plain, encoded, or encrypted source URLs.
1. Confirm the default page loads:
https://imgproxy.example.com
Enter fullscreen mode Exit fullscreen mode
2. Load a source image unprocessed (insecure/plain mode — fine for testing, not for production):
https://imgproxy.example.com/insecure/plain/https://images.pexels.com/photos/72594/japan-train-railroad-railway-72594.jpeg
Enter fullscreen mode Exit fullscreen mode
3. Resize it with the rs:fill:800:800 option (keeps aspect ratio while filling the target dimensions):
https://imgproxy.example.com/insecure/rs:fill:800:800/plain/https://images.pexels.com/photos/72594/japan-train-railroad-railway-72594.jpeg
Enter fullscreen mode Exit fullscreen mode
4. Convert format with @webp (check the Content-Type response header in devtools → Network to confirm):
https://imgproxy.example.com/insecure/rs:fill:800:800/plain/https://images.pexels.com/photos/72594/japan-train-railroad-railway-72594.jpeg@webp
Enter fullscreen mode Exit fullscreen mode
5. Add a watermark with watermark:1:ce:2:2 (enabled, centered, 2/2 x/y offset):
https://imgproxy.example.com/insecure/rs:fill:800:800/watermark:1:ce:2:2/plain/https://images.pexels.com/photos/72594/japan-train-railroad-railway-72594.jpeg
Enter fullscreen mode Exit fullscreen mode
See the full processing options reference for everything else supported.
Protect ImgProxy with Signed URLs
Without a key/salt pair, anyone can hit ImgProxy with arbitrary options. Signing locks requests to specific, pre-approved transformations.
1. Generate a key and salt:
$ openssl rand -hex 32
$ openssl rand -hex 16
Enter fullscreen mode Exit fullscreen mode
2. Add them to the config:
$ sudo nano /usr/local/bin/imgproxy.env
Enter fullscreen mode Exit fullscreen mode
IMGPROXY_KEY=your-generated-key
IMGPROXY_SALT=your-generated-salt
Enter fullscreen mode Exit fullscreen mode
3. Script a signature generator:
$ nano imgproxy-signing.sh
Enter fullscreen mode Exit fullscreen mode
#!/bin/bash
KEY_VALUE="your-generated-key"
SALT_VALUE="your-generated-salt"
KEY=$(echo -n "$KEY_VALUE" | xxd -r -p)
SALT=$(echo -n "$SALT_VALUE" | xxd -r -p)
URL_PATH="/rs:fill:800:800/watermark:1:ce:2:2/plain/https://images.pexels.com/photos/72594/japan-train-railroad-railway-72594.jpeg"
IMGPROXY_URL="https://imgproxy.example.com/"
DATA="$SALT$URL_PATH"
SIGNATURE=$(echo -n "$DATA" | openssl dgst -sha256 -hmac "$KEY" -binary | base64 -w 0 | tr '+/' '-_' | tr -d '=')
echo "The Signature based on your key/salt pair is: $SIGNATURE \n"
echo "The Signed ImgProxy URL is: $IMGPROXY_URL$SIGNATURE$URL_PATH"
Enter fullscreen mode Exit fullscreen mode
$ bash imgproxy-signing.sh
$ sudo systemctl restart imgproxy
Enter fullscreen mode Exit fullscreen mode
Visit the generated signed URL to confirm it works. Reusing that signature with different processing options (e.g. adding blur:10) gets rejected with a 403 Invalid signature — one signature is bound to one exact transformation.
Trust a signature for reuse across multiple source URLs:
$ sudo nano /usr/local/bin/imgproxy.env
Enter fullscreen mode Exit fullscreen mode
IMGPROXY_TRUSTED_SIGNATURES=your-signature-here
Enter fullscreen mode Exit fullscreen mode
Comma-separate multiple trusted signatures. Restart ImgProxy and confirm the trusted signature now works against different source images/formats — useful when a dynamic app (WordPress, etc.) needs to serve many distinct images without regenerating a signature per URL.
$ sudo systemctl restart imgproxy
Enter fullscreen mode Exit fullscreen mode
Process Images from S3-Compatible Object Storage
ImgProxy can pull directly from S3-compatible buckets instead of arbitrary URLs.
$ sudo nano /usr/local/bin/imgproxy.env
Enter fullscreen mode Exit fullscreen mode
IMGPROXY_USE_S3=true
IMGPROXY_S3_ENDPOINT=your-region.your-provider.com
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
Enter fullscreen mode Exit fullscreen mode
Reference bucket objects with the s3://bucket_name/file-name URI in place of an HTTP source URL:
https://imgproxy-url/processing-options/s3://bucket-name/file-name
Enter fullscreen mode Exit fullscreen mode
Next Steps
ImgProxy is running behind Nginx with TLS, URL signing, and object-storage support. From here:
- Set
IMGPROXY_TTLand CDN caching headers to avoid reprocessing the same transform repeatedly - Add Prometheus + Grafana to track processing latency and request volume
- Move
IMGPROXY_KEY/IMGPROXY_SALTinto a secrets manager instead of the plain env file for production
For the full guide, visit the original article on Vultr Docs.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.