โก JXLify
A Next-Generation Image Proxy & Origin Server in Rust
Serving JPEG XL (.jxl), AVIF (.avif), WebP (.webp), and Legacy Formats on the Fly with 100% In-Process Rust Codecs, Smart Content Negotiation, and Cache Acceleration.
๐ Overview
JXLify is a high-performance image proxy and origin web server written in Rust, designed as a modern, memory-safe, asynchronous evolution of webp_server_go.
JXLify sits as a middle-man in the loop between your users/CDN and your raw image assets. When a client requests an image (e.g. https://example.com/photos/landscape.jpg), JXLify dynamically negotiates client support using HTTP Accept headers and User-Agent heuristics, converts the image on the fly to the optimal format (JPEG XL, AVIF, or WebP), caches the generated asset and its metadata on disk, and serves it with instant response times and minimal CPU usage.
๐งญ Format Fallback Matrix & Negotiation Strategy
The Fallback Hierarchy
JXLify negotiates image formats using a 4-tier hierarchy:
$$\mathbf{JPEG\ XL\ (jxl)} ;\longrightarrow; \mathbf{AVIF\ (avif)} ;\longrightarrow; \mathbf{WebP\ (webp)} ;\longrightarrow; \mathbf{Original\ (JPEG/PNG/GIF/BMP)}$$
Targeted Single-Format Encoding (CPU-Efficient)
Instead of converting all formats simultaneously on every request (which wastes CPU and increases latency), JXLify determines the single highest-priority format the client supports and converts/serves only that format:
- Client supports JPEG XL (
image/jxl): Serves cached JXL or encodes to JXL. - Client supports AVIF (
image/avif): Serves cached AVIF or encodes to AVIF. - Client supports WebP (
image/webp): Serves cached WebP or encodes to WebP. - Client supports none of the above: Serves the original raw image (or resized raw image).
โก Redundant Conversion Skip
If the source raw image is already in the negotiated format (e.g. source is already photo.webp and client accepts WebP) and no resizing/cropping is requested, JXLify skips conversion entirely and serves the file directly with 0ms CPU overhead.
๐๏ธ Unified Storage Layout & 2-Level Hash Sharding
All data is cleanly organized under the data/ directory:
data/pics: Original source images.data/cache: Unified disk cache directory (safe to delete at any time):data/cache/images/: Converted.jxl,.avif,.webpand resized variants (2-level sharded).data/cache/metadata/: JSON metadata & BlurHash strings (2-level sharded).data/cache/remote/: Downloaded upstream images in reverse proxy mode (2-level sharded).
๐๏ธ Understanding & Configuring Storage Paths
In config.toml, only two paths are needed:
= "./data/pics" # Source images (local path or remote URL)
= "./data/cache" # Unified cache (safe to delete anytime)
| Config Key | Purpose | Typical Value | Description |
|---|---|---|---|
img_path |
Original Image Source | "./data/pics" or "https://cdn.example.com" |
Directory (or upstream URL) containing raw source images. |
cache_path |
Unified Disk Cache | "./data/cache" |
Cache directory for all generated data (images/, metadata/, remote/). Safe to delete at any time (rm -rf ./data/cache). |
How to Setup Storage
Mode 1: Local Origin Mode (Self-Hosted Images)
If your original image files are hosted on the local server or mounted volume:
- Put your source images into
./data/pics/(or any path like/var/www/uploads/). - Set
img_path = "./data/pics". - Set
cache_path = "./data/cache". - Requests to
http://localhost:3333/photos/cat.jpgwill resolve to./data/pics/photos/cat.jpgand cache to./data/cache/images/....
Mode 2: Remote Reverse-Proxy Mode (CDN / S3 / Cloud Storage)
If you want JXLify to sit in front of an existing remote image server:
- Set
img_path = "https://origin.example.com/assets". - Requests to
http://localhost:3333/photos/cat.jpgwill fetchhttps://origin.example.com/assets/photos/cat.jpg. - JXLify caches the raw file in
data/cache/remote/, converts it to the client's optimal format, caches the result indata/cache/images/, and serves it instantly.
Production Directory Sharding
In high-volume production environments with millions of images, flat directories cause severe filesystem inode lock contention and slow directory lookups. JXLify automatically implements 2-level hash sharding:
data/cache/images/local/
โโโ 8f/
โ โโโ 32/
โ โโโ 8f32d4639f7f415f.jxl
โ โโโ 8f32d4639f7f415f.avif
โ โโโ 8f32d4639f7f415f.webp
โโโ 01/
โ โโโ ee/
โ โโโ 01eef898821f07c6.webp
This distributes millions of files evenly across $256 \times 256 = 65,536$ subdirectories, maintaining $O(1)$ fast file lookups on all filesystems.
๐ฆ 100% In-Process Codecs & Transparency Preservation
No external CLI tools (FFmpeg / ImageMagick) are required. JXLify processes all formats entirely in-process:
1. Static Image Processing & Alpha Channels
- JPEG XL: In-process via
jpegxl-rs(libjxl) andjxl-encoder. Preserves full alpha transparency (RGBA8/RGBA16). - AVIF: Pure Rust via
ravif/rav1e(the engine behindcavif-rs). Preserves full alpha transparency. - WebP: In-process via
webpandimage/image-webp. Preserves alpha transparency. - JPEG / PNG / BMP / GIF: Pure Rust decoding and encoding via
image.
2. Animated GIF & WebP Processing
- Decoding: Multi-frame GIFs are parsed in-process via
image::codecs::gif::GifDecoder, extracting all frames, transparent palettes, and frame delays. - Encoding: Converted directly to animated WebP in-process using
webp-animation, preserving frame rates, loop counts, and alpha transparency. - Pass-through: Original animated GIF/WebP files are served untouched for legacy clients.
โก Key Features
- ๐ Asynchronous & Multi-Threaded: Built on Axum and Tokio with asynchronous I/O and Rayon thread pool for fast encoding.
- ๐ฏ Intelligent Content Negotiation: Evaluates
Acceptheader MIME types andUser-Agentheuristics (e.g. Safari 17+, iOS 17+, Firefox >= 93, Chrome). - ๐ On-The-Fly Conversion: Automatic conversion of
jpg,png,gif,bmp,svg,heic,nefintojxl,avif, orwebp. - ๐๏ธ Persistent Disk Caching with Hash Sharding: Caches optimized images and metadata in
CACHE_PATHwith 2-level directory sharding. - ๐ In-Flight Deduplication Lock: Uses async lock registry (
DashMap) to eliminate duplicate encoding when multiple concurrent requests hit the same uncached image. - ๐ Local Origin & Remote Proxy Modes: Can serve from local directory (
IMG_PATH: "./data/pics") or act as a reverse proxy for remote CDNs (IMG_PATH: "https://origin.example.com"). - ๐ On-Demand Resizing & Smart Cropping: Supports query parameters
?width=300&height=200,?max_width=800&max_height=600with multiple crop algorithms. - ๐ Image Metadata Endpoint: Append
?meta=fullto retrieve image dimensions, color profile, size, and BlurHash string. - ๐งน Automatic Cache Cleaner: Periodically enforces
MAX_CACHE_SIZEusing LRU / modification time pruning. - ๐ฆ Prefetching: Multi-threaded batch scanner (
--prefetch/--prefetch-foreground) to pre-warm the cache before production deployment. - ๐ HTTP Caching & Metrics: Sends
Vary: Accept, User-Agent, weakETag,Cache-Control, andX-Compression-Rate. - ๐ฉบ Health Check: Built-in
/healthzendpoint for Kubernetes and load balancer monitoring.
๐ Quick Start
Installation
Install via Cargo (Binary)
You can install the jxlify server binary directly from crates.io:
Once installed, verify the installation by running:
Use as a Library Dependency
Add jxlify to your project's Cargo.toml:
[]
= "0.1"
Or add it via the command line:
Build from Source
The compiled binary will be located at target/release/jxlify.
โ๏ธ Configuration Reference
Generate a default config.toml:
Full Configuration Table
| Option | Type | Default | Description | Environment Override |
|---|---|---|---|---|
host |
String |
"0.0.0.0" |
IP address to bind (use 127.0.0.1 for localhost only, 0.0.0.0 for all interfaces). |
JXLIFY_HOST |
port |
String/Int |
"3333" |
TCP port for the HTTP server. | JXLIFY_PORT |
quality |
Integer |
80 |
Compression quality (1โ100). 80 offers visually lossless quality; 100 triggers true lossless encoding for JXL and WebP. |
JXLIFY_QUALITY |
allowed_types |
Array |
["jpg", "png", ...] |
Whitelist of allowed image extensions. Requests for other extensions return 400 Bad Request. Use ["*"] to allow all. |
JXLIFY_ALLOWED_TYPES |
convert_types |
Array |
["jxl", "avif", "webp"] |
Enabled modern output formats. Order does not matter (fallback priority is always JXL -> AVIF -> WebP). Remove a format (e.g. ["webp"]) to disable it. |
JXLIFY_CONVERT_TYPES |
strip_metadata |
Boolean |
true |
When true, strips EXIF, GPS coordinates, and camera profiles from output files to minimize size and protect privacy. |
JXLIFY_STRIP_METADATA |
img_path |
String |
"./data/pics" |
Root directory or remote HTTP/HTTPS upstream URL for source images. | JXLIFY_IMG_PATH |
cache_path |
String |
"./data/cache" |
Unified cache directory for all generated data (images/, metadata/, remote/). |
JXLIFY_CACHE_PATH |
enable_extra_params |
Boolean |
false |
When true, enables on-demand dynamic resizing via query parameters (?width=, ?height=, ?max_width=, ?max_height=). |
JXLIFY_ENABLE_EXTRA_PARAMS |
crop_interesting |
String |
"InterestingAttention" |
Focal point algorithm for aspect-ratio crops. Options: InterestingAttention, InterestingEntropy, InterestingCentre, InterestingNone. |
JXLIFY_EXTRA_PARAMS_CROP_INTERESTING |
cache_ttl |
Integer |
2592000 |
Browser cache TTL in seconds (default 2592000 = 30 days). Sent in Cache-Control: max-age=.... |
JXLIFY_CACHE_TTL |
max_cache_size |
Integer |
0 |
Maximum disk cache size in Megabytes (e.g. 10240 for 10 GB). 0 = unlimited. Background cleaner automatically removes oldest LRU files when exceeded. |
JXLIFY_MAX_CACHE_SIZE |
read_buffer_size |
Integer |
4096 |
Internal I/O read buffer size in bytes for streaming files. | JXLIFY_READ_BUFFER_SIZE |
concurrency |
Integer |
262144 |
Maximum concurrent request worker pool size. | JXLIFY_CONCURRENCY |
disable_keepalive |
Boolean |
false |
Set to true to close TCP connection after each request (Connection: close). |
JXLIFY_DISABLE_KEEPALIVE |
Detailed Setting Explanations
1. Image Quality (quality = 80)
- Controls lossy compression density across all encoders (1โ100).
- 80 is the recommended default, providing ~70โ85% file size reduction with zero visible artifacting.
- 100 activates lossless compression mode in libjxl (JPEG XL) and libwebp (WebP).
2. Format Whitelist & Target Formats
allowed_types: Security control preventing arbitrary file serving. Only extensions in this list will be processed (e.g.jpg,jpeg,png,gif,bmp,svg,heic,nef,webp,avif,jxl).convert_types: Allows selectively disabling newer formats if desired. For example, settingconvert_types = ["avif", "webp"]disables JXL conversion even if client supports it.
3. Dynamic Resizing & Smart Cropping (enable_extra_params = true)
When enabled, JXLify dynamically resizes images based on URL query parameters:
GET /photo.jpg?width=400&height=300: Resizes and crops to exact 400x300 dimensions using the configured smart crop algorithm (crop_interesting).GET /photo.jpg?max_width=800&max_height=600: Proportionally fits image within bounding box while preserving original aspect ratio.
4. Cache Cleanup & Quotas (max_cache_size)
- If
max_cache_size = 10240(10 GB), a lightweight background cleaner runs every 60 seconds. - If total cache size exceeds the limit, the cleaner automatically purges the oldest, least-recently-modified files (LRU policy) until cache size is within limits.
- Also cleans stale
.tmp.*temporary files from interrupted writes.
๐ CLI Usage
# Start server with default ./config.toml
# Specify custom config file
# Prefetch all images in background while server runs
# Prefetch all images in foreground and exit
# Print version
๐ง Running as a Daemon (Systemd Service)
A production-ready systemd unit file is provided at jxlify.service.
1. Standard System Installation
# 1. Install binary to /usr/bin/
# 2. Install default configuration to /etc/jxlify/
# 3. Install systemd service unit
# 4. Reload systemd daemon
2. Enable & Start
# Start and enable JXLify on boot
# Check service status
3. View Logs
# Stream live logs
๐ก HTTP API & Headers
Requesting Images
# Request image (Safari 17+ will receive image/jxl, Chrome will receive image/avif, older browsers get image/webp or JPEG)
# Request image with on-the-fly resizing
&height=300
# Inspect metadata & BlurHash
Response Headers Example
HTTP/1.1 200 OK
Content-Type: image/jxl
Content-Length: 42150
Vary: Accept, User-Agent
ETag: W/"a9b8c7d6e5"
Cache-Control: public, max-age=31536000, immutable
X-Original-Format: jpeg
X-Served-Format: jxl
X-Compression-Rate: 0.38
Server: JXLify
๐ License
Licensed under the Apache License, Version 2.0 (LICENSE).