Skip to content

Instantly share code, notes, and snippets.

@eXtrem0us
Last active April 15, 2024 13:47
Show Gist options
  • Save eXtrem0us/5e6ade6e9325437e7e9a261e114e2c0e to your computer and use it in GitHub Desktop.
Save eXtrem0us/5e6ade6e9325437e7e9a261e114e2c0e to your computer and use it in GitHub Desktop.
Nginx Image Filter with Caching for Upstream
########################################################################################################
# This config enables us to:
# - Resize
# - Rotate
# ...the resident images on an upstream server, with Nginx Server in our hand, On the fly!
# - Just by using arbitrary Query Parameters
# ...and
# - Cache the transformed image by address.
#
# Well, I got the Idea from https://stumbles.id.au/nginx-dynamic-image-resizing-with-caching.html
# But some fundamental changes are applied.
# I customized this config to be used along with the Minio object storage.
#
########################################################################################################
# Samples:
# http://example.com/some/direcories/photo.jpg?w=200&r=90
# Resizes the photo down to width 200px and rotates it 90 clockwise degrees, keeps aspect ratio.
#
# https://example.com/some/other/dirs/photo2.png?w=200&h=100
# Resizes the photo same as the above, but according to keep aspect ratio,
# it chooses the most size decreesing parameter between width or height.
########################################################################################################
# =====================================================================================================#
###################
##### Caching #####
###################
# Well. Let's define a cache that works.
proxy_cache_path /tmp/nginx-images-cache levels=1:2 keys_zone=images:10m inactive=720h max_size=1024m;
proxy_cache_key "$scheme$request_method$host$request_uri$is_args$arg_w$arg_h$arg_r$arg_cx$arg_cy$arg_q";
proxy_cache_valid 200 302 10m;
###################
##### Mapping #####
###################
## Web Socket ##
map $http_upgrade $connection_upgrade {
default upgrade;
"" close;
}
## Nginx Image Filter ##
# If the HTTP Method is not GET:
# Forward the request to the Minio Server, directly.
# If the HTTP Method is GET:
# Well, it may be an image and we may need to apply the Nginx Image Filter Rules on:
map $request_method $preferred_proxy
{
default http://minio:9000; # The Minio Server or any external addresses.
GET http://127.0.0.1:8888$request_uri; # Somewhere that transforms the images. You would know. Be patient! (^_^)
}
# Use the defined cache only if the HTTP Method is GET.
# Otherwise we would encounter some serious problems, when other HTTP Methodes reach to the cache! Be Patient! (o_0)
# Cache the image, if and only if the method is GET and one of the transform args (h,w,cx,cy,q,r) are sent:
map $request_method$arg_w$arg_h$arg_r$arg_cx$arg_cy$arg_q $preferred_cache
{
default off;
~^GET\d+ images;
}
# Change the width of the photo, only if:
# the 'w' query parameter is set
# and it is an integer.
# Otherwise ignore it!
map $arg_w $imgfltr_w
{
default -;
~\d+ $arg_w;
}
# Change the height of the photo, only if:
# the 'h' query parameter is set
# and it is an integer.
# Otherwise ignore it!
map $arg_h $imgfltr_h
{
default -;
~\d+ $arg_h;
}
# Rotate the photo, only if:
# the 'r' query parameter is set
# and it is an integer.
# Otherwise ignore it!
# Note: Nginx Image Filter just supports { 90 , 180 , 270 }.
# In the other cases, Nginx Image Filter ignores the integer by itself. :)
map $arg_r $imgfltr_r
{
default -;
~\d+ $arg_r;
}
# Change the quality of the converted photo, only if:
# the 'q' query parameter is set
# and it is an integer less than or equal of 100
# Otherwise ignore it!
# This parameter is applied both for JPEG and WebP formats
map $arg_q $imgfltr_q
{
default -;
~\d+ $arg_q;
}
# Break the Comment in case of emergency usage, cropping photos!
#map $arg_cx $imgfltr_cx
#{
# default -;
# ~\d+ $arg_cx;
#}
#
#map $arg_cy $imgfltr_cy
#{
# default -;
# ~\d+ $arg_cy;
#}
## IMGProxy ##
map $request_uri $pretransformed_uri
{
default $request_uri;
~^(?<origpic>.*)\.avif $origpic;
}
map $arg_w $imgproxy_w
{
default 0;
~\d+ $arg_w;
}
map $arg_h $imgproxy_h
{
default 0;
~\d+ $arg_h;
}
map $arg_r $imgproxy_r
{
default 0;
~(90|180|270) $arg_r;
}
###################
#### Upstreams ####
###################
#If you have any IMGProxy Server:
upstream aviftransformer {
server imgproxy:8080;
}
server {
# This is where the images are Transported: rotate, resize, crop.
server_name _;
listen 8888;
location / {
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_set_header Host $http_host; #It is essential to send host header. 'cause minio uses whole hostname and query params to generate and validate secure links. same like as Nginx SecureLink Module
proxy_pass http://minio:9000;
image_filter resize $imgfltr_w $imgfltr_h; # Do you remember the defined mappings? Here it is!
image_filter rotate $imgfltr_r; # Same as the above.
#image_filter crop $imgfltr_cx $imgfltr_cy; # You know what to do.
image_filter_jpeg_quality $imgfltr_q; # Size matters! If you know, you know! ;)
image_filter_webp_quality $imgfltr_q; # Size matters! If you know, you know! ;)
image_filter_transparency on; # We don't want to lose the transparrency of PNG and GIF photos.
image_filter_buffer 8M; # Buffer matters for big photos. That's enough for most cases.
error_page 415 = /empty; # To change error 415 to 404
add_header X-Powered-By "Mehdi Hamidi (@eXtrem0us)"; # If you liked it, Buy me a Beer.
}
location = /empty {
#empty_gif;
return 404;
}
}
# Here's where all the requests are served and classified.
server {
listen 80;
server_name _;
# To allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 0;
# To disable buffering
proxy_buffering off;
## Nginx Image Filter ##
##### We're gonna classify the image requests of the other ones.
##### If you serve animated GIFS on your storage, remove the '|gif' part out of the below line.
location ~* "/.*\.(jpe?g|png|webp|gif)$" {
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_set_header Host $http_host;
proxy_connect_timeout 300;
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
add_header X-Proxy-Cache $upstream_cache_status;
proxy_pass $preferred_proxy; # If the HTTP Method is GET && We wanted to get an image file... (^_^)
proxy_cache $preferred_cache; # Use the cache, if Method is GET && We wanted to get an image file... (o_0)
}
## IMGProxy ##
location ~* "/.*\.(jpe?g|png|webp|gif|svg|ico|bmp|tiff)\.avif$" {
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_set_header Host $http_host;
proxy_connect_timeout 300;
proxy_read_timeout 600;
proxy_set_header Accept "*/*"; #to disable vary from multiple browsers
proxy_ignore_headers Vary; #to disable vary from multiple browsers
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1.1;
add_header X-Proxy-Cache $upstream_cache_status;
proxy_pass http://aviftransformer/_/s:$imgproxy_w:$imgproxy_h:0:0/rot:$imgproxy_r/plain/http://minio:9000$pretransformed_uri@avif;
proxy_cache images;
proxy_cache_lock on;
}
##### For the rest of the requests:
location / {
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_set_header Host $http_host;
proxy_connect_timeout 300;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
proxy_pass http://minio:9000; # If you are using docker-compose this would be the hostname i.e. minio
# Health Check endpoint might go here. See https://www.nginx.com/resources/wiki/modules/healthcheck/
# /minio/health/live;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment