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:
- A client requests an image with transformation parameters encoded in the URL
- NGINX fetches the original image (from disk or an upstream proxy)
- The small light header filter detects the
small_light()function in the URI - The body filter accumulates the full image body into a buffer
- ImageMagick processes the image according to the requested parameters
- NGINX delivers the transformed image with the correct
Content-Typeheader
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_moduledirective 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 todw=200,dh=200,da=lbecomes 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 exactlydwxdh.
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-Typeheader 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:
- The source file exists and is a valid image
- The
load_moduledirective is at the top ofnginx.conf - The
small_light on;directive is active in the correct context
Transformations Not Applied
If images pass through unchanged, check:
- The
small_light on;directive is set in the rightserverorlocationblock - The URL matches the
small_light()function pattern - 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.

