Images are great and I want to have a lot of them. Only problem is the hassle to resize and organize them to be web friendly. I found that nginx had a module to resize images and cache the result but all the configs I found to do so were problematic or insufficient in some way, so I wrote my own.
Installing the image_filter module
Instructions copied from Nginx prebuilt Debian packages (also has instructions for other distros).
From the Debian Repo
While it should be fine to just install libnginx-mod-http-image-filter
by doing
sudo apt update
sudo apt install libnginx-mod-http-image-filter
From the Nginx Repo
For reasons I cannot remember I am using the Nginx prebuilt Debian package from the official Nginx repo.
- Install prereqs
sudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring
- Fetch the nginx signing key and verify that the downloaded file contains the correct key (check here if viewing in 2027)
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \ | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
gpg --dry-run --quiet --no-keyring --import --import-options \ import-show /usr/share/keyrings/nginx-archive-keyring.gpg
pub rsa4096 2024-05-29 [SC] 8540A6F18833A80E9C1653A42FD21310B49F6B46 uid nginx signing key <[email protected]> pub rsa2048 2011-08-19 [SC] [expires: 2027-05-24] 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 uid nginx signing key <[email protected]> pub rsa4096 2024-05-29 [SC] 9E9BE90EACBCDE69FE9B204CBCDCD8A38D88A2B3 uid nginx signing key <[email protected]>
- Add the repo to
apt
and use repository pinning to prefer the Nginx repo over the Debian repoecho "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/debian `lsb_release -cs` nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \ | sudo tee /etc/apt/preferences.d/99nginx
- Update then install
nginx-module-image-filter
sudo apt update sudo apt install nginx-module-image-filter
Using the image_filter Module
Note: if you are building Nginx from scratch you can enable the image_filter
module during build time
and don’t need to load it at runtime like here.
In the /etc/nginx/nginx.conf
add the following line to the outermost scope (outside stream
, http
, or the such)
load_module modules/ngx_http_image_filter_module.so;
Note that ngx
is not a typo. Check that the configuration is OK.
nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Adding the Image Server to Nginx
My personal requirements for the config:
- resize images to certain sizes and cache the result
- retrieving the original image when no argument is set
- serve files other than images from the same host
# image resizing server
server {
listen 28956;
server_name 127.0.0.1;
root /srv/website/static; # TODO: your path
# I can only hope you don't have a directory called "invalid-option"
location ~ "^/invalid-option/" {
return 404;
}
location ~ "^/resize/(?<image>.+)$" {
alias /srv/website/static/$image;
image_filter resize $arg_width -;
image_filter_jpeg_quality 75;
image_filter_buffer 15M; # max image size
}
}
# cache settings
proxy_cache_path /tmp/nginx-images-cache/ levels=1:2 keys_zone=images:32m inactive=48h max_size=512m;
# image serving server
server {
listen 443 ssl;
http2 on;
server_name static.koitu.com; # TODO: your host
ssl_certificate <your fullchain>; # TODO
ssl_certificate_key <your privkey>; # TODO
root /srv/website/static; # TODO: your path
set $resize_option "/invalid-option";
# only when width is unset do we return the image in original size
if ($arg_width = "") {
set $resize_option "";
}
# only allow a limited selection of resolutions to resize into
if ($arg_width ~ "^(240|480|720|1280)$") {
set $resize_option "/resize";
}
location ~* "\.(png|jpg|jpeg)$" {
proxy_pass http://127.0.0.1:28956$resize_option$request_uri;
# cache all returned images
proxy_cache images;
proxy_cache_valid 200 48h;
}
}
In this config I specified a max cache size of 0.5 GB since my website is stored on a HDD and my cache is on a SSD. I am also only allowing resizing to 240px, 480px, 720px, and 1280px but its up to you how you want to configure your own setup.
After you are happy check the config with nginx -t
then service nginx reload
then place an image in the path
and test it out by either opening in your browser or using wget.
Example: my domain is static.koitu.com
and say I placed the file test.jpg
at /srv/website/static/test.jpg
then I can visit
https://static.koitu.com/test.jpg?width=240
https://static.koitu.com/test.jpg?width=480
https://static.koitu.com/test.jpg?width=720
https://static.koitu.com/test.jpg?width=1280
https://static.koitu.com/test.jpg
Adding Responsive Images to Your Website
See here for more details.
In my case, some of my images at original size are too big to really use (unless you are viewing my page in 8k for some reason). As a result, the only options I give are for viewing at the reduced resolutions.
The following is an example of using hugo
{{ $src := .Params.iamge }}
<img src="{{ $src }}?width=1280"
srcset="{{ $src }}?width=1280 1280w,
{{ $src }}?width=720 720w,
{{ $src }}?width=480 480w,
{{ $src }}?width=240 240w"
loading="lazy">
Which generates the following HTML that uses to use responsive images
<img src="https://static.koitu.com/test.jpg?width=1280"
srcset="https://static.koitu.com/test.jpg?width=1280 1280w,
https://static.koitu.com/test.jpg?width=720 720w,
https://static.koitu.com/test.jpg?width=480 480w,
https://static.koitu.com/test.jpg?width=240 240w"
loading="lazy">