Skip to main content

NGINX

NGINX Small Light Module: Dynamic Image Transformation Guide

by ,


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

Every website serves images. And on most websites, images account for the majority of page weight. When a user on a mobile phone requests your 4000×3000 product photo, the full-resolution file transfers over a slow connection — wasting bandwidth, inflating load times, and tanking your Core Web Vitals scores. The NGINX small light module eliminates this problem by transforming images on the fly, directly inside NGINX, with zero pre-processing.

The obvious alternative — pre-generating thumbnails and responsive variants — creates its own headaches. You need a build pipeline to produce every size, a storage strategy for the variants, and a way to keep them in sync when originals change. Add WebP conversion to the mix and the number of variants multiplies further.

With the NGINX small light module, you encode transformation parameters directly in the URL, and NGINX applies them as the image passes through its response pipeline. One original file, unlimited variants, no external service required.

How the NGINX Small Light Module Works

The NGINX small light module operates as an output filter. It sits in the response processing pipeline and intercepts image responses before they reach the client. Here is the processing flow:

  1. A client requests an image with transformation parameters encoded in the URL
  2. NGINX fetches the original image (from disk or an upstream proxy)
  3. The small light header filter detects the small_light() function in the URI
  4. The body filter accumulates the full image body into a buffer
  5. ImageMagick processes the image according to the requested parameters
  6. NGINX delivers the transformed image with the correct Content-Type header

The module supports three image processing backends:

Backend Strengths Limitations
ImageMagick Full feature set, WebP support, progressive JPEG, CMYK conversion, icon embedding Highest memory usage
Imlib2 Lightweight, fast for simple operations No GIF/WebP output, no blur, no unsharp mask
GD Smallest footprint, pure C No blur, no unsharp mask, limited format support

The GetPageSpeed extras repository packages the module with the ImageMagick backend, which provides the most complete feature set.

Installation

RHEL, CentOS, AlmaLinux, Rocky Linux

sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-small-light

Then load the module by adding this directive at the top of /etc/nginx/nginx.conf, before any other configuration blocks:

load_module modules/ngx_http_small_light_module.so;

For more details, see the RPM module page.

Debian and Ubuntu

First, set up the GetPageSpeed APT repository, then install:

sudo apt-get update
sudo apt-get install nginx-module-small-light

On Debian/Ubuntu, the package handles module loading automatically. No load_module directive is needed.

For more details, see the APT module page.

Basic Configuration

The simplest configuration enables the module and defines a location block that captures the transformation parameters from the URL:

server {
    listen 80;
    server_name images.example.com;
    root /var/www/images;

    small_light on;

    location ~ small_light[^/]*/(.+)$ {
        set $file $1;
        rewrite ^ /$file;
    }
}

With this configuration, you can request transformed images using URL parameters:

http://images.example.com/small_light(dw=300,dh=300)/photos/landscape.jpg

This request tells NGINX to serve photos/landscape.jpg resized to fit within 300×300 pixels while maintaining the aspect ratio.

Configuration Directives Reference

The NGINX small light module provides seven directives. All have been verified against the module source code and tested with nginx -t on a production build.

small_light

Enables or disables image transformation for the current context.

Syntax: small_light on | off;
Default: off
Context: http, server, location

location /images/ {
    small_light on;
}

small_light_getparam_mode

Switches from URL function syntax to standard query parameters. When enabled, transformation parameters are read from the query string instead of the small_light() function in the URL path.

Syntax: small_light_getparam_mode on | off;
Default: off
Context: http, server, location

server {
    small_light on;
    small_light_getparam_mode on;

    location /img/ {
        # Requests use query params: /img/photo.jpg?dw=200&dh=200
    }
}

This mode is useful when you cannot control URL paths or when integrating with applications that use query parameters for image sizing.

small_light_pattern_define

Defines a named preset of transformation parameters. Presets reduce URL complexity and ensure consistent image variants across your site.

Syntax: small_light_pattern_define name parameters;
Default: none
Context: server

server {
    small_light on;
    small_light_pattern_define thumb dw=150,dh=150,da=l,q=85,e=imagemagick,jpeghint=y;
    small_light_pattern_define hero dw=1200,dh=600,da=l,q=90,e=imagemagick,progressive=y;
    small_light_pattern_define webp_thumb dw=150,dh=150,of=webp,q=80,e=imagemagick;

    location ~ small_light[^/]*/(.+)$ {
        set $file $1;
        rewrite ^ /$file;
    }
}

Use the p parameter to reference a preset:

/small_light(p=thumb)/photos/portrait.jpg
/small_light(p=hero)/banners/homepage.jpg

You can still override individual parameters when using a preset:

/small_light(p=thumb,q=50)/photos/portrait.jpg

small_light_buffer

Sets the maximum buffer size for reading images when the response lacks a Content-Length header. Increase this value if you handle large images or if upstream servers use chunked transfer encoding.

Syntax: small_light_buffer size;
Default: 1m
Context: http, server

server {
    small_light_buffer 5m;
}

small_light_radius_max

Caps the maximum radius value for sharpen, unsharp, and blur operations. This prevents clients from requesting computationally expensive operations that could overload the server.

Syntax: small_light_radius_max value;
Default: 10
Context: http, server, location

server {
    small_light_radius_max 15;
}

When a request specifies a radius larger than this limit, the operation is silently skipped and NGINX logs a warning:

As sharpen geometry is too large, ignored.

small_light_sigma_max

Caps the maximum sigma value for sharpen, unsharp, and blur operations. Works alongside small_light_radius_max to limit processing intensity.

Syntax: small_light_sigma_max value;
Default: 10
Context: http, server, location

server {
    small_light_sigma_max 15;
}

small_light_material_dir

Specifies the directory containing icon images used by the embedicon parameter for watermarking.

Syntax: small_light_material_dir path;
Default: none
Context: server

server {
    small_light_material_dir /srv/watermarks;
}

Transformation Parameters

The NGINX small light module accepts 32 transformation parameters, either in the small_light() URL function or as query parameters when small_light_getparam_mode is enabled. Every parameter listed below has been verified against the module source code.

Dimensions and Positioning

Parameter Type Default Description
dw coordinate original width Destination width. Append p for percentage (e.g., 50p = 50%)
dh coordinate original height Destination height. Append p for percentage
dx coordinate 0 Destination X offset
dy coordinate 0 Destination Y offset
da character l Aspect ratio mode: l (long edge), s (short edge), n (no preservation)
ds character n Scaling: s (force scale even if larger), n (only downscale)

Aspect ratio modes explained:

  • da=l — The destination dimensions constrain the long edge. An 800×600 image resized to dw=200,dh=200,da=l becomes 200×150.
  • da=s — The destination dimensions constrain the short edge. The same image becomes 266×200.
  • da=n — No aspect ratio preservation. The image is stretched to exactly dwxdh.

Source Cropping

Parameter Type Default Description
sw coordinate Source crop width
sh coordinate Source crop height
sx coordinate Source crop X offset
sy coordinate Source crop Y offset

Crop a 400×300 region starting at position (100, 50):

/small_light(sw=400,sh=300,sx=100,sy=50,dw=400)/photos/wide.jpg

Canvas and Border

Parameter Type Default Description
cw number Canvas width
ch number Canvas height
cc color 000000 Canvas color (rrggbb or rrggbbaa)
bw number Border width
bh number Border height
bc color 000000 Border color

Create a 300×300 canvas with a red background and center the resized image on it:

/small_light(dw=200,dh=200,cw=300,ch=300,cc=ff0000)/photos/product.jpg

Filters and Effects

Parameter Type Default Backend Description
sharpen string ImageMagick, Imlib2, GD Sharpen with radiusxsigma format (e.g., 2x5)
unsharp string ImageMagick only Unsharp mask: radiusxsigma+amount+threshold (e.g., 2x5+0.5+0)
blur string ImageMagick, Imlib2 Gaussian blur: radiusxsigma (e.g., 5x10)

Format and Quality

Parameter Type Default Description
of string original format Output format: jpg, png, gif, webp
q number 0 (library default) Output quality, 1–100
progressive character n Progressive JPEG encoding (ImageMagick only)
jpeghint character n JPEG hinting optimization (ImageMagick, Imlib2)

Advanced Parameters

Parameter Type Default Description
e string imagemagick Processing engine selection
p string Named preset reference
angle number 0 Rotation angle: 90, 180, or 270 degrees
pt character n Pass-through mode: y skips all transformation
embedicon string Icon filename from small_light_material_dir (ImageMagick only)
ix number 0 Icon X position
iy number 0 Icon Y position
cmyk2rgb character n Convert CMYK to sRGB (ImageMagick only)
rmprof character n Remove color profile (ImageMagick only)
autoorient character n Auto-orient based on EXIF data (ImageMagick 6.9.0+)

Performance Optimization

Image transformation is CPU-intensive work. A poorly configured NGINX small light setup can quickly exhaust server resources under load. The following strategies keep the module performant in production.

Enable JPEG Hinting

JPEG hinting is the single most impactful performance optimization available. When enabled, ImageMagick decodes only enough data from the source JPEG to produce the target resolution, rather than fully decoding the original and then downscaling.

small_light_pattern_define thumb dw=150,dh=150,q=85,jpeghint=y;

For a typical scenario where you downscale a 4000×3000 photograph to a 150×150 thumbnail, JPEG hinting can reduce processing time significantly because ImageMagick skips most of the source image decoding.

JPEG hinting only applies when the source format is JPEG. It has no effect on PNG or GIF inputs.

Limit ImageMagick Thread Usage

The module automatically limits ImageMagick to one thread per worker process during initialization. This is critical because NGINX uses a multi-process architecture where each worker handles many concurrent requests. Without this limit, ImageMagick’s internal OpenMP threading would compete with NGINX’s event loop, causing context-switch overhead and degraded throughput.

This thread limiting happens automatically — no configuration is required. However, you should be aware that increasing NGINX worker_processes effectively increases your image processing parallelism.

Cap Filter Radii

Blur and sharpen operations scale quadratically with the radius parameter. A malicious or misconfigured request with blur=100x100 can consume several seconds of CPU time for a single image.

Always set conservative limits:

server {
    small_light_radius_max 10;
    small_light_sigma_max 10;
}

These defaults are adequate for most use cases. Values above 15 rarely produce visibly better results but dramatically increase processing cost.

Size the Buffer Correctly

The small_light_buffer directive controls how much memory NGINX allocates when the upstream response lacks a Content-Length header (common with chunked transfer encoding from application backends).

If the buffer is too small, image processing fails. If it is too large, you waste memory across all worker connections.

server {
    # Match your largest expected source image
    small_light_buffer 5m;
}

A 5 MB buffer handles most high-resolution photographs. Increase to 10-20 MB only if you serve extremely large source images (RAW exports, high-res scans).

Cache Transformed Images

The NGINX small light module does not cache transformed results. Every request processes the image from scratch. For production deployments, you must add a caching layer.

Option 1: NGINX proxy_cache

Place a caching reverse proxy in front of the image transformation server:

proxy_cache_path /var/cache/nginx/images levels=1:2
                 keys_zone=img_cache:10m max_size=1g
                 inactive=7d use_temp_path=off;

server {
    listen 80;
    server_name images.example.com;

    location / {
        proxy_cache img_cache;
        proxy_cache_valid 200 7d;
        proxy_cache_key "$request_uri";
        proxy_pass http://127.0.0.1:8081;
    }
}

This approach is highly effective because the cache key includes the full URI with transformation parameters, so each unique image variant is cached independently.

Option 2: CDN caching

If you use a CDN like Cloudflare or Fastly, configure it to cache image responses by URL. The transformation parameters in the URL create unique cache keys naturally.

For additional static file caching strategies, see NGINX SlowFS Cache.

Use Named Presets

Named presets reduce parsing overhead and prevent clients from requesting arbitrary transformation combinations. Define a fixed set of allowed variants:

server {
    small_light on;
    small_light_pattern_define thumb dw=150,dh=150,q=85,jpeghint=y;
    small_light_pattern_define card dw=400,dh=300,q=85,jpeghint=y;
    small_light_pattern_define hero dw=1200,dh=600,q=90,progressive=y;

    location ~ small_light[^/]*/(.+)$ {
        set $file $1;
        rewrite ^ /$file;
    }
}

Then use presets in URLs instead of raw parameters:

/small_light(p=thumb)/img/photo.jpg
/small_light(p=hero)/img/banner.jpg

Using with Proxy Backends

In many production architectures, NGINX proxies requests to an upstream image storage server (S3, object storage, or an application backend). The NGINX small light module works seamlessly as an output filter on proxied responses.

upstream image_backend {
    server storage.internal:8080;
}

server {
    listen 80;
    server_name images.example.com;

    small_light on;
    small_light_pattern_define thumb dw=150,dh=150,q=85,jpeghint=y;
    small_light_buffer 5m;

    location ~ small_light[^/]*/(.+)$ {
        set $file $1;
        proxy_pass http://image_backend/$file;
    }
}

The module intercepts the upstream image response, applies transformations, and delivers the result to the client. This architecture decouples image storage from image processing.

WebP Conversion

Converting JPEG and PNG images to WebP format reduces file sizes by 25–35% with equivalent visual quality. The NGINX small light module handles this conversion with a single parameter:

/small_light(dw=800,of=webp,q=80)/photos/landscape.jpg

The most practical way to serve WebP automatically is to define paired presets — one for the original format and one for WebP:

server {
    small_light on;
    small_light_pattern_define thumb dw=150,dh=150,q=85,jpeghint=y;
    small_light_pattern_define thumb_webp dw=150,dh=150,of=webp,q=80;
    small_light_pattern_define hero dw=1200,dh=600,q=90,progressive=y;
    small_light_pattern_define hero_webp dw=1200,dh=600,of=webp,q=85;

    location ~ small_light[^/]*/(.+)$ {
        set $file $1;
        rewrite ^ /$file;
    }
}

Then let your application or CDN select the appropriate preset based on the browser’s Accept header. This approach avoids complex rewrite logic and gives you full control over quality settings per format.

For complementary compression optimization, see NGINX Gzip Compression and Brotli Compression on NGINX.

Security Best Practices

Protect Against Resource Exhaustion

Without restrictions, clients can request arbitrary transformations that consume excessive CPU and memory. Implement these safeguards:

Limit allowed parameters with named presets. Instead of exposing raw transformation parameters, use presets and reject requests with unknown parameters:

server {
    small_light on;
    small_light_pattern_define thumb dw=150,dh=150,q=85,jpeghint=y;
    small_light_pattern_define medium dw=500,dh=500,q=90;
    small_light_radius_max 10;
    small_light_sigma_max 10;

    # Only allow preset-based requests
    location ~ small_light\(p=(?:thumb|medium)\)/(.+)$ {
        set $file $1;
        rewrite ^ /$file;
    }
}

This regex location block only matches requests using the thumb or medium presets, rejecting any attempt to specify arbitrary parameters.

Combine with rate limiting to prevent abuse:

limit_req_zone $binary_remote_addr zone=img_limit:10m rate=10r/s;

server {
    location ~ small_light[^/]*/(.+)$ {
        limit_req zone=img_limit burst=20 nodelay;
        set $file $1;
        rewrite ^ /$file break;
    }
}

Note the break flag on the rewrite — this is required for limit_req to work correctly within small light location blocks.

Prevent Hotlinking

Use the NGINX secure link module to sign image URLs and prevent unauthorized embedding on third-party sites.

Troubleshooting

“failed to get image type”

This error appears in the NGINX error log when the module receives a response that is not a recognized image format (JPEG, PNG, GIF, or WebP). Common causes:

  • The upstream returned an HTML error page instead of an image
  • The Content-Type header is missing or incorrect
  • The file is not actually an image

Check the upstream response independently:

curl -I http://backend/path/to/image.jpg

“small_light_buffer is small”

The image exceeds the configured buffer size. Increase small_light_buffer:

small_light_buffer 10m;

415 Unsupported Media Type

This status code indicates the module failed to process the image. Verify:

  1. The source file exists and is a valid image
  2. The load_module directive is at the top of nginx.conf
  3. The small_light on; directive is active in the correct context

Transformations Not Applied

If images pass through unchanged, check:

  1. The small_light on; directive is set in the right server or location block
  2. The URL matches the small_light() function pattern
  3. When using small_light_getparam_mode on, ensure the request includes query parameters

Large Radius Warning

As sharpen geometry is too large, ignored.

This means the requested sharpen, blur, or unsharp radius exceeds small_light_radius_max. The operation is skipped, and the image is returned without the filter. Increase the limit if needed, but consider the performance impact.

Animated GIF Limitation

The NGINX small light module processes only the first frame of animated GIFs. This is by design — processing every frame of an animation would multiply resource consumption by the number of frames. If you need to serve resized animated GIFs, consider handling them separately in your application layer.

Conclusion

The NGINX small light module provides a powerful, URL-driven approach to dynamic image transformation. By processing images directly inside NGINX, you eliminate the need for separate image processing services or pre-generated variants. Combined with JPEG hinting, buffer tuning, and a proxy cache layer, the module handles high-traffic image-heavy sites efficiently.

For production deployments, use named presets to restrict allowed transformations, cap filter radii to prevent resource exhaustion, and always place a cache in front of the transformation layer. The module’s integration as an NGINX output filter means it works transparently with any backend — static files, application servers, or object storage.

The module source code is available on GitHub.

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.