For quite some time now, inspired by the article Resizing Images on the Fly , image resizing with ngx_http_image_filter_module was set up and everything worked as it should. But there was one problem when the manager needed to get images with exact sizes for uploading to some services, because these were their technical requirements. For example, if we have an original image of size 1200x1200 , and when resizing we write something like ? Resize = 600x400 , we will get a proportionally reduced image at the smallest edge, size 400x400 . It is also impossible to obtain an image with a higher resolution (upscale). Those. ? resize = 1500x1500 will return all the same image1200x1200
An OpenResty article came to the rescue: we turn NGINX into a full-fledged application server to understand how Nginx works with Lua and the library for Lua isage / lua-imagick itself - Lua pure-c bindings to ImageMagick. Why such a solution was chosen, and not, say, something in python - because it is fast and convenient. You donβt even need to create any files, everything is right in the Nginx config (optional).
So what do we need
Examples will be given based on Debian.
apt-get update
apt-get install nginx-extras
Install LuaJIT
apt-get -y install lua5.1 luajit-5.1 libluajit-5.1-dev
Install imagemagick
apt-get -y install imagemagick
and the magickwand libraries to it, in my case for version 6
apt-cache search libmagickwand
apt-get -y install libmagickwand-6.q16-3 libmagickwand-6.q16-dev
Lua-imagick build
We clone the repository (well, or take the zip and unpack it)
cd ~
git clone https://github.com/isage/lua-imagick.git
cd lua-imagick
mkdir build
cd build
cmake ..
make
make install
If everything went well, you can configure Nginx.
backend , , , . Nginx, () . .
nginx backend config
server {
listen 8082;
listen [::]:8082;
set $files_root /var/www/example.lh/frontend/web;
root $files_root;
access_log off;
expires 1d;
location /files {
set $w 700;
set $h 700;
set $q 89;
if ($arg_q ~ "^([1-9]|[1-8][0-9])$") {
set $q $arg_q;
}
if ($arg_resize ~ "([\d\-]+)x([\d\+\!\^]+)") {
set $w $1;
set $h $2;
rewrite ^(.*)$ /resize/$w/$h/$q$uri last;
}
rewrite ^(.*)$ /resize/$w/$h/$q$uri last;
}
location ~* ^/resize/([\d]+)/([\d\+\!\^]+)/([\d]+)/files/(.+)$ {
default_type 'text/plain';
set $w $1;
set $h $2;
set $q $3;
set $fname $4;
content_by_lua '
local magick = require "imagick"
local img = magick.open(ngx.var.files_root .. "/files/" .. ngx.var.fname)
if not img then ngx.exit(ngx.HTTP_NOT_FOUND) end
img:set_gravity(magick.gravity["CenterGravity"])
if string.match(ngx.var.h, "%d+%+") then
local h = string.gsub(ngx.var.h, "(%+)", "")
resize = ngx.var.w .. "x" .. h
-- png
img:set_bg_color(img:has_alphachannel() and "none" or img:get_bg_color())
img:smart_resize(resize)
img:extent(ngx.var.w, h)
else
img:smart_resize(ngx.var.w .. "x" .. ngx.var.h)
end
if ngx.var.arg_q then img:set_quality(ngx.var.q) end
ngx.say(img:blob())
';
}
}
upstream imageserver {
server localhost:8082;
}
server {
listen 80;
server_name examaple.lh;
location ~* ^/files/.+\.(jpg|png) {
proxy_buffers 8 2m;
proxy_buffer_size 10m;
proxy_busy_buffers_size 10m;
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_pass http://imageserver;
}
}
, ( ) img:extent()
resize
+
.
:
- WxH (Keep aspect-ratio, use higher dimension)
- WxH^ (Keep aspect-ratio, use lower dimension (crop))
- WxH! (Ignore aspect-ratio)
- WxH+ (Keep aspect-ratio, add side borders)

Given the power and simplicity of this approach, you can implement things with a rather complicated logic, for example, add watermark'i or implement authorization with limited access. In order to find out the capabilities of the API for working with images, you can refer to the documentation of the isage / lua-imagick library