When a file arrives at the upload endpoint, JekCMS first checks its dimensions against the configured maximum (default: 1920px on the longest side). Files exceeding the limit are scaled down before any format conversion takes place. This pre-scaling step ensures the AVIF encoder works on a predictably sized input and prevents unexpectedly large output files.

EXIF Stripping and Secure Storage

EXIF stripping uses the GD or Imagick extension, depending on which is available on the server. All metadata is removed before the converted file is written to disk — GPS coordinates, camera model, creation timestamps, and any embedded thumbnails. The original file is stored at uploads/originals/ under a hashed filename and is never served over HTTP. The directory has no .htaccess rewrite rules, but the web server configuration blocks direct access by default.

Four Thumbnail Sizes and Quality Settings

Four thumbnail sizes are generated synchronously during upload: thumbnail (400×400 centre crop), medium (800px width, proportional height), large (1600px width, proportional height), and pinterest (1000×1500 face-aware crop). The AVIF quality setting is 80; WebP is 85. Both are configurable per-upload-context in config/media.php.

Serving Images in Custom Themes

In built-in themes, images are served via the <picture> element with AVIF as the primary source and WebP as the fallback. If you are building a custom theme, use the get_featured_image($post, 'medium') helper — it returns the correct source URL based on browser capability negotiation and handles the case where a thumbnail size was not generated.

The Complete Pipeline: Step by Step

The entire image processing pipeline executes synchronously during an upload request (or asynchronously when triggered via the media queue). Here is the exact sequence of operations from the moment a file reaches the server to the point where it is ready to serve.

1. Upload received → validate MIME type (jpeg, png, gif, webp, avif)
2. Check file size → reject if > 20MB (configurable)
3. Read dimensions → if longest side > 1920px, scale down
4. Strip EXIF metadata → remove GPS, camera info, timestamps
5. Generate AVIF (quality 80) → primary serving format
6. Generate WebP (quality 85) → fallback format
7. Generate 4 thumbnails (each in AVIF + WebP):
 - thumbnail: 400x400 center crop
 - medium: 800px width, proportional
 - large: 1600px width, proportional
 - pinterest: 1000x1500 face-aware crop
8. Store original → uploads/originals/{hash}.{ext}
9. Store conversions → uploads/images/{year}/{month}/
10. Write database record → media table with all paths
11. Delete temporary file → cleanup complete

File Size Reduction Numbers

Across 14,000 images processed through the pipeline in production, the average file size reductions are:

  • JPEG to AVIF: 62% smaller (average 340 KB to 129 KB)
  • JPEG to WebP: 41% smaller (average 340 KB to 201 KB)
  • PNG to AVIF: 78% smaller (average 1.2 MB to 264 KB)
  • PNG to WebP: 58% smaller (average 1.2 MB to 504 KB)

The Image Proxy Fallback

When a thumbnail variant does not exist on disk — for example, when an image was uploaded before the pinterest size was introduced — the get_featured_image() function falls back to the built-in image proxy at includes/image-proxy.php. The proxy generates the requested size on the fly, caches the result for 7 days, and serves it with proper cache headers.

// Image proxy URL format
/includes/image-proxy.php?path=images/2026/01/photo.avif&w=800&h=500

// Proxy internals:
// 1. Validate path (no directory traversal)
// 2. Check SSRF protection (block private IP ranges)
// 3. Check cache → serve if fresh
// 4. Resize using GD/Imagick → cache → serve
// 5. Garbage collection runs at 1% probability per request

Bulk Conversion From the Admin Panel

The Settings > Media page provides a bulk conversion tool for retroactively converting existing images. The process works in batches of 50 images to avoid memory limits on shared hosting. For each batch, the system identifies images that have a WebP version but no AVIF counterpart, generates the AVIF file, and updates the database record. Progress is displayed in real time via a polling endpoint that returns the current batch number and total remaining files.

Conversion Speed Benchmarks

  • Average conversion time per image (1200x800 JPEG to AVIF): 0.8 seconds on a VPS, 2.1 seconds on shared hosting
  • Batch of 50 images: 40 seconds VPS, 105 seconds shared hosting
  • Full retroactive conversion of 5,000 images: approximately 67 minutes on VPS
  • Memory usage during conversion: peaks at 128 MB for a 4000x3000 source image

Browser Support and Content Negotiation

AVIF is supported by Chrome 85+, Firefox 93+, and Safari 16.4+. For browsers without AVIF support, the <picture> element provides automatic fallback. JekCMS does not use server-side content negotiation (Accept header inspection) — the decision happens entirely in the browser via the <source> element's type attribute. This keeps the implementation simple and fully compatible with CDNs that do not forward the Accept header.

<picture>
 <source srcset="/uploads/images/photo.avif" type="image/avif">
 <source srcset="/uploads/images/photo.webp" type="image/webp">
 <img src="/uploads/images/photo.jpg" alt="Description"
 width="800" height="500" loading="lazy">
</picture>

Handling Animated and Transparent Images

The pipeline treats animated GIFs and transparent PNGs as special cases. Animated GIFs are converted to WebP only, because AVIF animation support remains inconsistent across browsers.

The original GIF is preserved alongside the WebP version so that email clients and older platforms can still display the animation. Transparent PNGs are converted to both AVIF and WebP with alpha channel preservation enabled, ensuring that logos, icons, and overlay graphics retain their transparency after conversion.