Last active
December 6, 2024 11:03
-
-
Save eXtrem0us/5e6ade6e9325437e7e9a261e114e2c0e to your computer and use it in GitHub Desktop.
Nginx Image Filter with Caching for Upstream
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
######################################################################################################## | |
# 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