Imagine uploading a 10 MB image to your site—a visitor on mobile data leaves before it loads. Heavy images are the main cause of poor LCP (Largest Contentful Paint) and high bounce rates. We've seen this dozens of times: clients complain about slow sites, but the culprit is unoptimized JPEGs with EXIF metadata, each 500 KB. Server-side image optimization solves this at the root: automatic lossy and lossless compression, conversion to modern formats like WebP and AVIF, dynamic resizing—all reducing data transfer by 40–70% without quality loss. Image optimization directly impacts Core Web Vitals, especially LCP and CLS, boosting search engine rankings. In one of our projects, a proper pipeline cut LCP from 4.2s to 1.1s for an e-commerce site with thousands of products. According to Wikipedia, WebP is an image format that provides superior compression.
How Server-Side Image Optimization Speeds Up Your Site
When an image is uploaded to the server, a pipeline runs: the Sharp or Intervention Image library resizes it to predefined maximum widths (400, 800, 1200, 1920 pixels), converts to WebP and AVIF, and strips EXIF data. The generated files are then cached on a CDN with a long TTL. The browser receives a small file in a supported format—the page loads instantly. Unlike client-side optimization (JavaScript compression), the server-side approach doesn't burden the user's device and works equally well for all traffic.
Why Choose WebP and AVIF?
| Format | Compression Type | Average Reduction | Browser Support |
|---|---|---|---|
| JPEG | Lossy | 1x (baseline) | 100% |
| WebP | Lossy/Lossless | 25-35% less than JPEG | 96% |
| AVIF | Lossy/Lossless | 50% less than JPEG | 85% |
AVIF offers better compression, WebP better compatibility. The optimal strategy is to generate both and deliver via <picture> with srcset.
Our Tech Stack for Server-Side Optimization
In Node.js projects we use the Sharp library—it provides high throughput and minimal memory consumption. For PHP applications we use Intervention Image with Imagick. Code examples are below.
Node.js: Sharp Pipeline
import sharp from 'sharp';
interface OptimizeOptions {
maxWidth?: number;
quality?: number;
format?: 'webp' | 'avif' | 'jpeg';
}
async function optimizeImage(inputBuffer: Buffer, options: OptimizeOptions = {}): Promise<Buffer> {
const { maxWidth = 1920, quality = 80, format = 'webp' } = options;
return sharp(inputBuffer)
.resize(maxWidth, undefined, {
withoutEnlargement: true,
fit: 'inside',
})
.toFormat(format, {
quality,
effort: 4, // balance speed/size
})
.withMetadata({ orientation: undefined }) // remove EXIF rotation
.toBuffer();
}
// Middleware for lazy optimization
app.get('/images/:key', async (req, res) => {
const { key } = req.params;
const { w, q = '80', f = 'webp' } = req.query;
// Check cache
const cacheKey = `${key}:${w}:${q}:${f}`;
const cached = await s3.getObject({ Key: `optimized/${cacheKey}.${f}` }).catch(() => null);
if (cached) {
return res.type(`image/${f}`).send(await streamToBuffer(cached.Body));
}
// Get original
const original = await s3.getObject({ Key: `originals/${key}` });
const buffer = await streamToBuffer(original.Body);
// Optimize
const optimized = await optimizeImage(buffer, {
maxWidth: w ? parseInt(w as string) : 1920,
quality: parseInt(q as string),
format: f as 'webp' | 'avif' | 'jpeg',
});
// Save to cache
await s3.putObject({
Key: `optimized/${cacheKey}.${f}`,
Body: optimized,
ContentType: `image/${f}`,
CacheControl: 'public, max-age=31536000',
});
res.type(`image/${f}`).send(optimized);
});
PHP: Optimization on Upload
use Intervention\Image\Facades\Image;
class ImageOptimizationService
{
const FORMATS = ['webp', 'avif'];
const SIZES = [400, 800, 1200, 1920];
public function process(UploadedFile $file): array
{
$image = Image::make($file->getPathname());
// Remove EXIF and rotate by orientation
$image->orientate()->stripExif();
$paths = [];
foreach (self::SIZES as $width) {
if ($image->width() < $width) continue;
$resized = clone $image;
$resized->resize($width, null, fn($c) => $c->aspectRatio()->upsize(false));
foreach (self::FORMATS as $format) {
$quality = $format === 'avif' ? 60 : 82;
$key = "images/{$width}w/{$this->generateKey()}.{$format}";
Storage::disk('s3')->put(
$key,
$resized->encode($format, $quality)->__toString(),
['CacheControl' => 'public, max-age=31536000']
);
$paths[$format][$width] = $key;
}
}
return $paths;
}
}
HTML: Responsive Images with srcset
// Blade helper for displaying optimized images
function responsive_img(array $paths, string $alt, string $sizes = '100vw'): string
{
$avifSrcset = collect($paths['avif'] ?? [])->map(fn($path, $width) =>
Storage::disk('s3')->url($path) . " {$width}w"
)->join(', ');
$webpSrcset = collect($paths['webp'] ?? [])->map(fn($path, $width) =>
Storage::disk('s3')->url($path) . " {$width}w"
)->join(', ');
$fallback = Storage::disk('s3')->url(end($paths['webp']));
return <<<HTML
<picture>
<source type="image/avif" srcset="{$avifSrcset}" sizes="{$sizes}">
<source type="image/webp" srcset="{$webpSrcset}" sizes="{$sizes}">
<img src="{$fallback}" alt="{$alt}" loading="lazy" decoding="async">
</picture>
HTML;
}
Note the alt attribute: it should contain a keyword, e.g., "optimized product image," which also helps SEO.
Nginx: On-the-fly WebP Conversion
# Serve WebP if browser supports it
map $http_accept $webp_suffix {
"~*webp" ".webp";
default "";
}
server {
location ~* \.(jpg|jpeg|png)$ {
add_header Vary Accept;
try_files $uri$webp_suffix $uri =404;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Our Work Process
- Analysis — Audit current images: average size, formats, metadata. Identify growth areas.
- Pipeline Design — Choose strategy: on-the-fly (Nginx + ImageMagick) or pre-process on upload. Design caching system.
- Implementation — Write optimization code, configure upload handler, add srcset for responsive images.
- Testing — Verify visual quality, Core Web Vitals metrics, delivery speed. Use Lighthouse and PageSpeed Insights.
- Deployment — Deploy on server, integrate CDN (Cloudflare, AWS CloudFront), set up monitoring.
What's Included
- Integration of optimization pipeline (Sharp/Intervention) into your backend.
- Generation of multiple sizes (400, 800, 1200, 1920 px) for responsive images.
- Conversion to WebP and AVIF with optimal quality.
- Metadata stripping (EXIF) for additional savings.
- CDN caching with long-lived Cache-Control.
- Documentation for helper usage (Blade, Twig, JSX).
- Post-implementation support — 14 days of monitoring.
Checklist for evaluating your current pipeline:
- Use of modern formats (WebP/AVIF);
- Metadata stripping applied;
- Different sizes generated for responsive;
- CDN caching configured;
- Lazy loading and async decoding used.
Project Timelines
| Component | Estimated Time |
|---|---|
| Basic Sharp integration on upload | 2–3 days |
| On-the-fly conversion via Nginx | +1–2 days |
| Full pipeline with responsive srcset and CDN | 4–5 days |
Pricing is individualized based on your project — depend on stack, number of upload points, and caching requirements. For average traffic, the savings can be substantial. Contact us for an estimate and we'll propose the optimal solution.
Order a turnkey server-side image optimization solution — your site will become significantly faster. With over 7 years of experience, we've optimized images for more than 50 projects, from small blogs to high-traffic e-commerce stores. We guarantee a minimum 40% reduction in image size while preserving quality.







