Image Optimization

Automatically convert JPEG and PNG images to WebP or AVIF on the fly using the image optimization agent. The agent negotiates the best format from the client’s Accept header, caches converted images on disk, and falls back to the original on any error.

Use Case

  • Reduce image payload size by 30-80% with modern formats
  • Serve WebP to Chrome/Firefox/Safari and AVIF where supported
  • Cache converted images to avoid repeated CPU work
  • No changes needed to origin servers or image pipelines
  • Graceful fallback — the agent never makes a response worse

Architecture

                    ┌─────────────────┐
                    │    Clients      │
                    │  Accept: webp   │
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
                    │    Zentinel     │
                    │                 │
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
              ▼              ▼              ▼
         ┌─────────┐   ┌─────────┐   ┌──────────┐
         │  Image  │   │ Static  │   │   API    │
         │  Origin │   │ Assets  │   │ Backend  │
         └─────────┘   └─────────┘   └──────────┘
                             │
                    ┌────────▼────────┐
                    │  Image Opt      │
                    │  Agent          │
                    │  ┌───────────┐  │
                    │  │ FS Cache  │  │
                    │  └───────────┘  │
                    └─────────────────┘

Prerequisites

Install the image optimization agent:

# Via bundle (recommended)
zentinel bundle install image-optimization

# Or via cargo
cargo install zentinel-agent-image-optimization

Configuration

Create zentinel.kdl:

// Image Optimization Example
// Converts JPEG/PNG responses to WebP/AVIF with caching

system {
    worker-threads 4
}

listeners {
    listener "http" {
        address "0.0.0.0:8080"
    }
}

agents {
    agent "image-opt" type="custom" {
        unix-socket "/tmp/image-optimization.sock"
        events "request_headers" "response_headers" "response_body" "request_complete"

        timeout-ms 5000
        failure-mode "open"
        response-body-mode "buffer"
        max-response-body-bytes 10485760

        config {
            formats "webp" "avif"
            quality {
                webp 80
                avif 70
            }
            max_input_size_bytes 10485760
            max_pixel_count 25000000
            eligible_content_types "image/jpeg" "image/png"
            passthrough_patterns "\\.gif$" "\\.svg$" "\\.ico$"
            cache {
                enabled #true
                directory "/var/cache/zentinel/image-optimization"
                max_size_bytes 1073741824
                ttl_secs 86400
            }
        }
    }
}

upstreams {
    upstream "origin" {
        target "127.0.0.1:9000"
    }
}

routes {
    route "images" {
        matches { path-prefix "/" }
        upstream "origin"
        agents "image-opt"
    }
}

Running

Start the agent and proxy:

# Terminal 1: Start the image optimization agent
zentinel-image-optimization-agent \
    --socket /tmp/image-optimization.sock \
    --log-level info

# Terminal 2: Start Zentinel
zentinel -c zentinel.kdl

Testing

# Request a JPEG — should receive WebP if your client supports it
curl -H "Accept: image/webp,image/*" \
     -o optimized.webp \
     -D - \
     http://localhost:8080/photo.jpg

# Check response headers
# Content-Type: image/webp
# X-Image-Optimized: webp
# X-Image-Original-Size: 245760
# Vary: Accept

# Request again — should be a cache hit
curl -H "Accept: image/webp,image/*" \
     -o cached.webp \
     -D - \
     http://localhost:8080/photo.jpg
# X-Image-Optimized: cache-hit

# Request without WebP support — original passes through
curl -H "Accept: image/jpeg" \
     -o original.jpg \
     -D - \
     http://localhost:8080/photo.jpg
# Content-Type: image/jpeg (unchanged)

# Request AVIF
curl -H "Accept: image/avif,image/webp,image/*" \
     -o optimized.avif \
     -D - \
     http://localhost:8080/photo.jpg
# Content-Type: image/avif
# X-Image-Optimized: avif

Customization

WebP Only (Fastest)

If you don’t need AVIF support:

config {
    formats "webp"
    quality {
        webp 85
    }
}

Skip Large Images

Lower the size limits to avoid spending CPU on hero images:

config {
    max_input_size_bytes 5242880
    max_pixel_count 10000000
}

Exclude Specific Paths

Skip images that are already optimized or served from a CDN:

config {
    passthrough_patterns "\\.gif$" "\\.svg$" "/cdn/" "/thumbnails/"
}

gRPC Transport

For higher throughput or remote deployment:

agent "image-opt" type="custom" {
    grpc "http://image-opt-service:50060"
    events "request_headers" "response_headers" "response_body" "request_complete"

    timeout-ms 5000
    failure-mode "open"
}
zentinel-image-optimization-agent --grpc 0.0.0.0:50060

Response Headers

HeaderExampleDescription
Content-Typeimage/webpConverted format
X-Image-Optimizedwebp, avif, cache-hitConversion result
X-Image-Original-Size245760Original size in bytes
VaryAcceptDownstream caches vary by format