- performance
- core web vitals
- web development
How to make your website faster: 5 fixes that matter
Optimized images, lazy loading, less JavaScript, caching and Core Web Vitals: the five changes that make the biggest difference to your website's speed.

In this article
A visitor opens your website on their phone.
The screen stays blank. Then the text appears. Then an image pushes the text down. Then the button ignores the first tap.
Only a few seconds have passed, but that is enough for them to hit back.
Website speed almost never comes down to a single problem. It is usually the sum of many small choices: an oversized image, a script loaded for no reason, a resource downloaded again on every visit.
This guide covers 5 changes that really matter and, for each one, what to do in practice.
01. Optimize your images
Images are often the heaviest resource on a page. A photo taken on a phone and uploaded as is can weigh more than the rest of the site combined.
Three steps
Optimizing an image means working on three fronts:
- Dimensions: if the image is displayed 800 pixels wide, there is no point downloading one that is 4,000 wide.
- Compression: lossy compression works well for photos, as long as you check by eye that the quality holds up.
- Format: WebP compresses better than JPEG and PNG and is supported by every modern browser. In some tests AVIF saves more than 50% compared to JPEG.
Original image
↓
Resize
↓
Compress
↓
WebP or AVIF
↓
One version each for desktop, tablet and mobileServe the right version to every screen
With srcset and sizes, the browser picks the version that best fits the screen. With <picture> you can offer AVIF and WebP and keep JPEG as a fallback for browsers that do not support them:
<picture>
<source type="image/avif" srcset="photo-800.avif 800w, photo-1600.avif 1600w" sizes="(max-width: 800px) 100vw, 800px">
<source type="image/webp" srcset="photo-800.webp 800w, photo-1600.webp 1600w" sizes="(max-width: 800px) 100vw, 800px">
<img src="photo-800.jpg" alt="Photo description" width="800" height="533">
</picture>The width and height attributes do not resize the image: they let the browser reserve its space before it arrives, so the text does not jump down when the image shows up.
The rule to remember: never make visitors download an image larger than the one they will see.
02. Lazy load what sits below the fold
When a page opens, the visitor only sees the top part, the area "above the fold". Everything else they will see by scrolling, maybe.
There is no reason for the browser to download images at the bottom of the page right away, taking bandwidth from the ones needed now.
Native lazy loading
Today a single attribute does the job, supported by every major browser, for images and iframes alike:
<img src="gallery-1.webp" alt="Restaurant dining room" width="800" height="533" loading="lazy">The browser only downloads the image when the visitor gets close to that part of the page.
The opposite mistake
Lazy loading is only for what sits outside the initial screen. Applied to the main image at the top of the page, it slows it down: the browser waits to find out whether the image is visible before fetching it.
A study published on web.dev looked at WordPress pages that lazy loaded even the images visible right away. With lazy loading turned off, the main element appeared roughly 13 to 15% sooner.
The main image needs the opposite treatment: mark it as a priority.
<img src="hero.webp" alt="View of the venue" width="1200" height="800" fetchpriority="high">Above the fold → right away, high priority
↓
Below the fold → when the visitor gets close03. Cut unnecessary JavaScript
A heavy image slows down the download. JavaScript does more: it has to be downloaded, parsed, compiled and executed, and all of that ties up the browser's main thread.
While the main thread is busy, the page can look ready and still ignore taps. That is exactly the delay visitors experience as "the site is frozen".
Where the weight hides
- Unused dependencies: libraries installed for a feature that was later removed.
- Oversized single bundles: the whole application's code loaded even when the page only uses a small part of it.
- Third-party scripts: chat widgets, ad pixels, social embeds, analytics tools. Each one adds code you do not control.
What to do
Remove dependencies you no longer use. Split your code and load the non-essential parts only when they are needed, with a dynamic import():
button.addEventListener("click", async () => {
const { openMap } = await import("./map.js");
openMap();
});The map code is only downloaded if someone actually opens the map. For scripts that are not needed on first load, the defer attribute keeps them from blocking the page while it is parsed.
To find unused code, the Coverage tool in Chrome DevTools shows how much of each file actually runs during page load.
The rule: every kilobyte of JavaScript costs you twice, once to download it and once to run it.
04. Cache static assets
On the first visit the browser has to download everything: images, fonts, CSS, JavaScript. On the next visit, most of those resources are exactly the same.
Without a properly configured cache, the browser downloads them all over again.
First visit
↓
Download → store
↓
Next visit
↓
Reuse from cache, download only what changedTwo rules for two kinds of files
The trick is to separate files that change name when their content changes from files that do not.
Files with a fingerprint in their name, such as style.x234dff.css, which most modern frameworks generate automatically, can stay cached for a long time. When the content changes, so does the name, and the browser fetches the new file.
Cache-Control: max-age=31536000, immutableHTML, on the other hand, always lives at the same address. It should be stored, but checked with the server before every use:
Cache-Control: no-cacheno-cache does not mean "do not store". It means "store it, but ask before using it": if the page has not changed, the server replies with 304 Not Modified and the browser downloads nothing. To forbid storing a copy at all there is no-store, meant only for content that must not stay on the device.
05. Check your Core Web Vitals
Everything you have done so far needs to be measured. Core Web Vitals are the three metrics Google uses to assess a page's loading experience:
- LCP (Largest Contentful Paint): how long it takes to show the main element of the page. Good within 2.5 seconds.
- INP (Interaction to Next Paint): how quickly the page responds to taps, clicks and key presses. Good within 200 milliseconds. It replaced the older FID in March 2024.
- CLS (Cumulative Layout Shift): how much elements move around while the page loads. Good within 0.1.
A page passes when at least 75% of visits fall within the good range.
Lab data and real-world data
There are two main tools, and they measure different things:
- Lighthouse simulates a load on a mid-range phone over a mobile network. It is useful while you work, because it gives an instant, repeatable result.
- PageSpeed Insights also shows real data from Chrome users over the last 28 days, when the page has enough traffic. For a new or low-traffic site that data may be missing.
Measure
↓
Find the main problem
↓
Fix it
↓
Measure againThe cycle matters more than any single score. A one-off 100 on Lighthouse guarantees nothing if a 5 MB image lands on the homepage next week.
Speed is a feature
None of these changes makes a website fast on its own. You can have perfect images and a huge JavaScript bundle. You can have a flawless cache setup and a lazy loaded main image.
Optimized images
↓
Lazy loading below the fold
↓
Less JavaScript
↓
Cached static assets
↓
Measured Core Web Vitals
↓
Fast websiteSpeed comes from stacking these layers, and it is lost the same way: one small decision at a time.
Checklist for a faster website
Before publishing a page, check at least these five points:
- Images are compressed, in WebP or AVIF, and served at the size they are displayed.
- Images below the fold have
loading="lazy", the main image does not. - No library or third-party script is loaded without a clear reason.
- Fingerprinted static files have a long cache lifetime, HTML is revalidated.
- LCP, INP and CLS are measured on mobile, and again after every significant change.
This list will not fix every performance problem, but it covers the most common reasons a website is slow.
Speed is designed in
Speed is not something to fix at the end, once the site is live and someone complains. Every image you add, every library you install, every third-party script is a decision that weighs on loading time.
Nobody notices a fast website. People only notice when it is slow: that is why it pays to think about it before anyone does.
Sources
- web.dev, How the Core Web Vitals metrics thresholds were defined
- web.dev, Interaction to Next Paint becomes a Core Web Vital on March 12
- web.dev, Image performance
- web.dev, Browser-level image lazy loading for the web
- web.dev, The performance effects of too much lazy loading
- web.dev, Optimize resource loading with the Fetch Priority API
- web.dev, Code-split JavaScript
- web.dev, Prevent unnecessary network requests with the HTTP Cache
- Google for Developers, About PageSpeed Insights
Have a project in mind?
Hash42 Labs designs and builds web apps, websites and digital products for businesses, with security built in from day one. The same principles behind this article power MANJI and Muni.
