A product page looked fine on my office connection, then a launch-day check on a mid-range phone showed the hero image arriving late and pushing the headline down. The image was almost 3 MB, even though it displayed at less than half its original width. In this guide, I’ll cover how to resize and encode images, serve the right size for each screen, and check that the changes actually improve page loads.
Start with the image people see first
Open your page in Chrome DevTools, choose Network, and filter by Img. Check the image file sizes, dimensions, and request timing. Then use the Performance panel or PageSpeed Insights to see whether an image is the Largest Contentful Paint (LCP) element. That’s often the hero image, but it depends on the page.
Prioritize images that are large, visible early, or repeated across many pages. A 200 KB hero image can matter more than a 2 MB image halfway down a long article, because visitors may not scroll to it. Keep a copy of your originals somewhere safe; generate optimized files as separate outputs.
Resize before you compress
Don’t send a 4000-pixel-wide image to a page that displays it at 800 pixels. Resizing reduces the amount of image data the browser needs to download and decode. Leave room for high-density displays: if an image is displayed at 800 CSS pixels, a 1600-pixel version can look sharp on a 2× display.
For a quick JPEG workflow, install ImageMagick on Debian or Ubuntu:
sudo apt-get update
sudo apt-get install -y imagemagick
This creates a resized copy, preserves the aspect ratio, and avoids enlarging images smaller than the specified limit:
magick input.jpg -auto-orient -resize '1600x1600>' -strip -quality 82 output.jpg
-auto-orient applies the camera’s orientation metadata before removing metadata with -strip. Quality 82 is a sensible starting point, not a universal best setting. Compare the result at the size visitors will see it. Fine textures, text baked into an image, and gradients can show compression artifacts sooner than ordinary photos.
For a command-line WebP option, install cwebp on Debian or Ubuntu:
sudo apt-get install -y webp
Then create a resized WebP:
cwebp -resize 1600 0 -q 80 input.jpg -o output.webp
The zero height tells cwebp to preserve the aspect ratio. Keep the original until you’ve checked the result; a smaller file is not automatically a better-looking one.
A practical tip: inspect your output at actual display size, not just zoomed in. Compression artifacts that look alarming at 200% may be invisible in the page, while a blurry image can be obvious at normal size.
Serve responsive image sizes
A single optimized file can still be wasteful if every device downloads the largest version. Create a few widths from the same source—say 480, 960, and 1600 pixels—and let the browser select an appropriate candidate:
<img
src="/images/team-960.webp"
srcset="
/images/team-480.webp 480w,
/images/team-960.webp 960w,
/images/team-1600.webp 1600w"
sizes="(max-width: 600px) 100vw, 800px"
width="1600"
height="1067"
alt="The team working together at a table">
The sizes value describes the image’s expected rendered width. Here, it fills the viewport on narrow screens and is expected to render at up to 800 CSS pixels otherwise. Adjust that value to match your layout. The width and height attributes provide the image’s intrinsic aspect ratio, helping the browser reserve space and avoid layout shifts. Use the actual dimensions and aspect ratio of the source candidates.
For art direction—when the crop itself should change on small screens—use <picture>:
<picture>
<source
media="(max-width: 600px)"
srcset="/images/hero-mobile.webp">
<img
src="/images/hero-desktop.webp"
width="1600"
height="900"
alt="A runner on a forest trail">
</picture>
Use a meaningful alt description when the image conveys information. For purely decorative images, use alt="".
Choose formats with a fallback plan
WebP is supported by modern browsers and is often a good fit for photographic images. AVIF can produce smaller files in many cases, but encoding can take longer and results vary by image. I treat format choice as a measurement problem: encode representative images, compare visual quality, and check file sizes before changing the whole library.
The <picture> element allows a browser to choose a supported format, with a JPEG fallback:
<picture>
<source type="image/avif" srcset="/images/hero.avif">
<source type="image/webp" srcset="/images/hero.webp">
<img src="/images/hero.jpg" width="1600" height="900" alt="A runner on a forest trail">
</picture>
If you generate multiple widths for each format, provide matching srcset candidates in each <source>. Otherwise, a small-screen browser might still fetch a large file.
Load images at the right time
Lazy loading helps with images that are below the fold:
<img
src="/images/article-960.webp"
width="960"
height="640"
loading="lazy"
decoding="async"
alt="A close-up of a circuit board">
Don’t lazy-load the hero image or another image likely to be the LCP element. It needs to start downloading promptly:
<img
src="/images/hero-1600.webp"
width="1600"
height="900"
fetchpriority="high"
alt="A runner on a forest trail">
Use high priority sparingly—usually for one key image—rather than marking every image as urgent. Browser scheduling behavior varies, so verify the effect in a performance trace.
A less obvious tip: lazy loading can reduce the number of requests without improving the first screen if it’s applied only to images that were already out of view. Measure the LCP image separately; optimizing its size and discovery often matters more than adding loading="lazy" to every image.
Use a CDN where it makes sense
A content delivery network can serve images closer to visitors and, depending on the service, resize or convert them on request. That’s useful for sites with visitors in multiple regions or many image sizes to manage. It also adds another layer to configure and monitor: check cache behavior, URL variations, and whether the CDN is caching the optimized response as expected.
For projects I’ve run on VPS infrastructure, PowerVPS and Vast.ai have been options for hosting the origin server; image processing and CDN delivery still depend on the services configured for the project. If you’re comparing infrastructure for a deployment, the Server Rental Guide is another reference.
Check the result, not just the file size
After deploying, test the page on a throttled mobile connection and compare it with the previous version. Confirm that the browser downloads the intended image candidate in the Network panel, that the LCP image isn’t lazy-loaded, and that images don’t cause layout shifts. Also check pages with different layouts: a template change can make a previously appropriate sizes value inaccurate.
Keep an eye on image quality as content changes. A setting that works for landscape photography may damage screenshots or images containing small text. For those, use a more suitable format or a less aggressive quality setting.
Conclusion
A reliable image workflow is straightforward: resize to realistic display dimensions, encode and visually inspect, provide responsive candidates, and load each image according to when it’s needed. Measure the page after each change. That’s how you find the savings that matter to visitors without trading away image quality.
Top comments (0)