5 Easy Methods for Improving Your Website’s Front-End Performance in 2025

Many web developers and content managers will be pleasantly surprised by the web performance gains that can be achieved with minimal effort.

As a web developer, my goal isn’t just to build good-looking websites. It’s also to ensure that those same websites are efficient and performant, i.e., that they load as quickly as possible. Website optimization might not seem all that necessary given the increasing access to high-speed internet connections. My response to that is four-fold:

  1. Research shows that website users are impatient, and will only wait a few seconds before getting frustrated and moving on.
  2. No one will ever complain that your website loads too quickly, even if they’ve got a gigabit internet connection. So why not make it as fast as you possibly can?
  3. Equal access to high-speed internet connections is still lacking even in 2025, especially outside the U.S. — which is a factor if your website is intended for global audiences.
  4. It actually requires very little effort to make some significant improvements to your website’s performance.

With regards to my final point, website optimization can seem daunting, especially when CDNs, caching techniques, and server enhancements enter the discussion. Those are not unimportant, but there are also some very simple methods that you can employ on the front-end with minimal HTML knowledge and/or while managing your website’s content. So simple, in fact, that there’s no real reason not to do them.

In this post, I’ll be focusing on these five methods:

  1. Optimize Your Images
  2. Optimize Your Front-End Code
  3. Optimize Your Web Fonts
  4. Reduce HTTP Requests
  5. Prioritize What the Browser Loads

Note: I intentionally kept this article pretty high-level. Wherever possible, though, I linked to additional resources in case you want to dig deeper into the details of a particular method. Now, let’s make our websites faster, shall we?


1) Optimize Your Images

Since the web is a visual medium, images dominate, be they product photos, headshots, photos of pets, or silly animated GIFs. If you have a website, then chances are, it has more than a few pretty pictures. But even as those images make your website more appealing and informative, they could also be slowing it down.

In my experience, it’s not uncommon for clients to upload photos that they took with their phone or downloaded from a stock photo site like Unsplash. Those images are usually far bigger than they need to be, resulting in much slower page loads. But this is a problem that’s easily solved.

A) Use the Right Image Format

There are four primary formats used for web images: AVIF, JPEG, PNG, and WebP. GIFs are still around, especially the animated variety, but have lost ground as AVIF, PNG, and WebP have grown in popularity. Picking the right image format, however, depends on the image in question. (As with many things development-related, these guidelines are not set in stone, but do hold true more often than not.)

Image FormatUse for
AVIF Photos
Animated images
Transparent images
Images that can be compressed (i.e., don't need to be the highest quality)
JPEG Photos and non-animated images
Images that can be compressed (i.e., don't need to be the highest quality)
PNG Logos
Line art
Transparent images
Images that need to be the highest quality possible (i.e., no compression)
WebP Photos
Animated images
Transparent images
Images that can be compressed (i.e., don't need to be the highest quality)

AVIF, JPEG, and WebP are ideal for photos because they support “lossy” compression. That is, they reduce an image’s file size by reducing its visual quality to a certain degree. PNG files, on the other hand, are “lossless.” Their compression doesn’t result in a loss of image quality. As a result, PNG file sizes are almost always considerably higher than AVIF, JPEG, and WebP file sizes, especially for photos. (Note: AVIF and WebP also support “lossless” compression.)

You might also see mention of SVG, which is often used for logos, icons, and illustrations. Unlike AVIF, JPEG, PNG, and WebP images, which are saved as a grid of individual pixels, an SVG image is essentially a series of coordinates saved as XML that map out the image’s geometry. (That’s why SVG is called a “vector” format while those others are called “raster” or “bitmap” formats.) One advantage of SVG images is that they can be resized infinitely with no quality loss, since they’re just coordinates to be recalculated and redrawn. SVG can also be manipulated by CSS and JS to achieve visual effects like animation.

B) Resize and Crop Your Images

Images that are taken with a phone or downloaded from a stock photo site will likely be huge. For example, a photo taken with my iPhone is 4,032 x 3,024 pixels in size, and stock photos can be even bigger. The bigger an image’s dimensions, however, the bigger its file size. What’s more, those dimensions are almost certainly far bigger than your website needs or requires.

Thus, you should ways resize your images before adding them to your website. How much resizing, though, depends on the images’ use and placement. If an image is a headshot or product photo, then you might be able to use a smaller size. On the other hand, a background image that fills up a large space (like a page masthead) probably shouldn’t be resized as much.

What’s more, you probably don’t need the entire image. Especially in the case of a headshot or product photo, there might be a bunch of empty space around the photo’s subject that can removed via cropping. Not only does cropping reduce the image’s overall size, but it can also better highlight the subject matter.

Resizing and cropping can be done via image editors like Photoshop or GIMP. However, you can also use your computer’s built-in image tool (e.g., Preview on Mac OS) or an online tool like Pixlr. Regardless of whether you resize, crop, or both, the goal is to reduce your image’s dimensions as much as possible without obscuring its subject matter.

C) Compress Your Images

I’ve already written about image optimization at length, so I won’t go into great detail here. But essentially, the last thing you should do to an image before adding it to your website is run it through a compression utility like Squoosh, my current fave.

Note: Some hosting providers offer built-in image compression as part of their hosting packages. Even if that’s the case for you, it’s still good practice to compress images before uploading them.

Depending on the image’s size, format, and visual characteristics (e.g., color or grayscale, sharp or blurry), you can see some dramatic savings. I’ve seen file size reductions by as much as 80% and even close to 90% with almost no significant loss in image quality. But even if you only see a 10 – 20% reduction, that’s still 10 – 20% less information that your users have to download, and every bit helps.

As a side benefit, compressed images take up less server space, potentially reducing your hosting bill and/or allowing you to host more for your money.


2) Optimize Your Front-end Code

Just as you can optimize your website’s images, you can also optimize your website’s HTML, CSS, and JavaScript. Specifically, you can “minify” it, i.e., remove all line breaks, tabs, and unnecessary spaces and other characters to make your file sizes as small as possible.

Consider this CSS:

.foo {
	margin: 1rem;
	padding: 1rem;
	border: 1px solid red;
	box-shadow: 10px 10px 5px 0px rgba(0, 0, 0, 0.75);
}

.bar {
	padding: 1rem;
	background-color: rgba(224, 224, 224, 1);
	color: rgba(0, 128, 255, 1);
}

It weights in at 214 bytes. Here’s that same CSS minified:

.foo{margin:1rem;padding:1rem;border:1px solid red;box-shadow:10px 10px 5px 0 rgba(0,0,0,.75)}.bar{padding:1rem;background-color:rgb(224,224,224);color:rgb(0,128,255)}

Although the above code looks like gibberish, browsers will have no problem interpreting and executing it. And by removing unnecessary spaces, line breaks and tabs, and even simplifying some CSS properties, like coverting rgba to rgb, that CSS is now 167 byes. This is an obviously small example, but minifying all of your website’s CSS, as well as its HTML and JavaScript, can result in some impressive savings.

Fortunately, you don’t need to do this by hand (although you certainly can if you’re feeling really nerdy). There are numerous code minification utilities available online; just search for “minify css” or “minify js.” Also, if your website’s running on a CMS like WordPress or Craft, there are third-party plugins that’ll do the minification for you. Finally, your hosting provider might also offer code minification as part of your hosting package.

As with images, the goal here is to reduce the size of your website’s code so that a user’s browser downloads as little as possible. The less the browser has to download, the quicker your website will load.


3) Optimize Your Web Fonts

I can still remember when web typography was limited to web-safe fonts like Arial, Georgia, and Verdana. These days, however, the sky’s the limit thanks to web fonts. Services like Google Fonts, Bunny Fonts, and Adobe Fonts make it easier than ever to spruce up your website with creative, professional, and even whimsical fonts.

But it’s possible to have too much of a good thing, and with it, negatively impact your website’s performance. Like images, web fonts can be hefty in size, and the more web fonts you employ, the bigger a speed bottleneck they represent. Fortunately, as with images, there are some simple steps you can take to counteract that.

A) Use as Few Web Fonts as Possible

When you’re first starting out with a design, it’s tempting to include as many web fonts as possible, and as many variations of each font. After all, why limit yourself? If you’re not careful, though, your web font usage can quickly get out of hand.

For example, let’s say you’re using Google Fonts, and you’ve selected IBM Plex Serif, Poppins, and Roboto for your site’s design. That’s three fonts. Each font, however, has multiple styles, like “Thin,” “Thin Italic,” “Medium,” and “Medium Italic.” Those three specific fonts, in fact, contain 44(!) variations between them.

Modern browsers are usually smart enough to only load those variations that are actually used by a specific webpage. Even so, it’s wise to exercise restraint and include only the necessary variations, even combining some in your design. (Do you really need both “Thin” and “Extralight” Poppins, or can you get away with just one of them?)

B) Use Variable Web Fonts

Rather than have an individual file for each specific variation, variable web fonts combine all of a font’s possible variations into just one or two files. Montserrat is a popular variable web font that consists of just two files, one for “normal” Montserrat and one for “italic” Montserrat. But each file contains all possible weights, from “Thin” and “ExtraLight” to “ExtraBold” and “Black,” meaning you don’t need individual files for each specific style and weight combination.

Variable web font files are usually larger than normal web font files because they contain the information of multiple variations, but since there are fewer files in total, that means fewer server requests. (More on server requests in a moment.) That, and variable fonts give you ultimate flexibility in determining your website’s typography. (Opus’ current design, by the way, uses a variable version of Alegreya.)

C) Self-host Your Web Fonts

As with image optimization, I’ve previously written about this at length, so I won’t repeat myself here, either. Basically, services like Google Fonts make it easy to use web fonts in part by hosting the font files for you. However, it’s more advisable to download those files using a tool like Mario Ranftl’s Google Webfonts Helper and host them yourself.

Self-hosting removes the need for a user’s browser to also connect to the servers of the web font service, and fewer server connections is always a good thing. Self-hosting also addresses the potential privacy implications of using a service like Google Fonts. Finally, if you’re feeling particularly ambitious, you can even customize your web font files to further reduce their file size (e.g., remove unused and unnecessary characters).

If you do self-host your web fonts, make sure to use WOFF 2.0 versions. WOFF 2.0 is the most modern web font format, and it ensures that your file sizes will be as small as possible.


4) Reduce HTTP Requests

When a browser loads a webpage, it’s not just making one request. A single webpage actually contains numerous individual components or resources: the base HTML file as well as images, stylesheets, scripts, and even audio and video files. After the browser has identified everything that makes up a particular webpage, acquiring each one of those resources is a multi-step process:

  1. The browser requests a resource (e.g., an image, a stylesheet) from the server.
  2. The server attempts to find the requested resource and return its location (or, if it can’t be found, return an error).
  3. The browser downloads the resource and takes the appropriate action. If it’s an image, the browser loads the image so the user can see it. If it’s a stylesheet, the browser analyzes the file and applies any relevant styles to the current webpage’s elements. And so on…

This all happens behind the scenes, and if you’re on a good connection, in a few seconds. (If you want to see the number of resources for a specific webpage, open up your browser’s element inspector, switch to the “Network” tab, and refresh the webpage.) But excessive server requests can bog things down, especially if those requests are spread across multiple servers. If the webpage contains a YouTube video, for example, then the browser must also make requests to YouTube’s server in addition to the webpage’s server.

You obviously can’t control requests that are made to third parties. But when it comes to your own website, you want to reduce the overall number of requests that a browser makes whenever it accesses one of your website’s pages. As the saying goes, the fastest server request is the one that’s never made.

Note: Once a file’s been downloaded, it’s usually stored in the browser’s cache. Which means that on subsequent visits to the same webpage or another webpage on the same site, the browser will use that cached version rather than download the file again. This is a good thing and we ought to leverage browser caching. But we should still seek to improve that initial page load as much as possible.

A) Combine Your Website’s Files

I previously recommended minifying your website’s files. You can also combine multiple files, resulting in fewer total files. This is helpful if you’re using a CMS. It’s not uncommon for a WordPress theme to require multiple CSS and JavaScript files, especially if it has fancy widgets like sliders and modals. For the record, I’ve seen WordPress sites that load dozens of CSS and JavaScript files. (This can happen with other CMSs, but in my experience, it’s a near-certainty with WordPress.)

True, those files might only be a few kilobytes apiece, but each one represents a server request, which adds up over time. Thus, the obvious solution is to combine all of your website’s stylesheets into a single CSS file, and likewise, combine all of your scripts into a single JavaScript file. If you’re using WordPress, a plugin like W3 Total Cache will do this for you. Otherwise, you can attempt to do it manually.

Note: When combining CSS or JavaScript files, the order in which you combine the files can matter. You’ll need to do some testing to identify and fix any conflicts. But it’s worth it, since a successful combination means a leaner, more efficient website.

B) Inline Your CSS and JavaScript

Most of the time, your website’s CSS and JavaScript will be placed in their own files that are called in the HTML via <link> and <script> tags. You can, however, place that code directly in your HTML, usually in the <head> or before the </body> tag. (If you ever see the term “critical CSS,” it refers to this process.)

This “inlining” of CSS and JavaScript removes the need to request and download external files, but it can increase the size of your webpage’s HTML. Depending on your website’s set-up and needs, though, as well as the amount of CSS and JavaScript it uses, that might be an acceptable trade-off.

Other methods for reducing HTTP requests include switching to variable fonts (see above); using image sprites to combine multiple images into a single, larger image; and switching from images to inline SVG (especially for icons and logos). There’s a lot of room for experimentation and creativity here, with the ultimate goal being to reduce the number of lines in the aforementioned “Network” tab as much as possible.


5) Prioritize What the Browser Loads

Webpages are complicated things, and to their credit, modern browsers do a pretty decent job of handling it all. When a browser loads a webpage, it does its best to organize and prioritize what needs to be downloaded. Obviously, stuff that gets downloaded earlier will be processed and displayed first. Sometimes, however, you might want browsers to behave a little differently, and thankfully, it’s possible to give them a nudge in the right direction.

A) Lazy Loading

Let’s say you have a webpage with lots of images, like a list of blog posts or an image gallery. By default, a browser will start downloading all of those images as soon as it sees them referenced in the HTML. Which, on the surface, makes sense. But what about images lower on the webpage? Why should the browser waste time and bandwidth downloading those images if they’re not immediately visible, or if the user never actually scrolls down to see them?

This is where the loading HTML attribute is a godsend. This attribute allows you to communicate the relative importance of images, telling the browser which ones should be loaded as expected and which ones can be deferred. The loading attribute has two possible values — eager and lazy — which are implemented like so:

<img src="foo.jpg" alt="" width="10" height="10" loading="eager">

<img src="bar.jpg" alt="" width="20" height="20" loading="lazy">

The eager value tells the browser to load the image instantly, even if it’s not in that part of the webpage that’s currently visible in the browser window. (This is often called the “viewport.”) This is the default behavior. But lazy tells the browser to deprioritize the image and load it only when necessary, i.e., if it nears the viewport as the user scrolls down the webpage.

I use lazy throughout Opus, and especially on the thumbnails on archive pages like this one. You can see how lazy works via your browser’s element inspector. Switch to the “Network” tab and then to the image-only view. Refresh the browser and you’ll see all of the images that have been downloaded. That list, however, doesn’t include every image that’s actually on the webpage. As you scroll down, new images suddenly appear in the list, meaning the browser is only now downloading them as they’ve become necessary. Those images were initially ignored, however, resulting in a faster initial page load — which is precisely what we want.

Note: The loading attribute can also be used for iframes, and functions similarly. An <iframe> element with loading="lazy" will defer loading its content until scrolling brings it nearer to the viewport.

B) Fetch Priority

Similar to the loading attribute, the fetchpriority attribute can give you some control over how a browser prioritizes loading certain resources. Unlike loading, though, fetchpriority can be applied to a wide range of resources, including images, fonts, and <link> and <script> tags. Also, fetchpriority has three values — high, low, and auto — which can used thusly:

<img src="foo.jpg" alt="" width="10" height="10" fetchpriority="high">

<link rel="stylesheet" href="foo.css" fetchpriority="low">

<script src="foo.js" fetchpriority="auto"></script>

Using fetchpriority="high" tells the browser to prioritize a resource and load it as quickly as possible. Similarly, using fetchpriority="low" tells the browser to de-prioritize the loading of a resource. And finally, fetchpriority="auto" tells the browser to choose whatever priority it thinks best, which is the default behavior. (If you really want to dig deep into Fetch Priority, Web.dev has a very in-depth article on the topic.)

Just remember that high and low aren’t absolute values. It’s still ultimately up to the browser to determine what’s prioritized or not, but fetchpriority can nudge it one way or the other. Also, use fetchpriority="high" judiciously, resisting the urge to add it to a bunch of resources. Remember: When everything’s prioritized, nothing’s prioritized.

A good use case for fetchpriority="high" is for hero images, or for the first image in a slideshow. Another use case might be to de-prioritize non-essential scripts or styles that aren’t immediately necessary, like so:

<script src="important.js" fetchpriority="high" async></script>

<script src="unimportant.js" fetchpriority="low"></script>

Unlike loading="lazy", which can be easily implemented all over your website for an immediate result, I’ve found that implementing fetchpriority is a process of trial and error. If your webpage is on the complex side, you might easily find yourself experimenting with different variations of fetchpriority on different resources, and using your developer tools to ascertain which variations are the most effective.

C) Preload Necessary Resources

As the name suggests, preloading instructs a browser to download and save resources as early as possible in the loading process, even before it begins rendering the webpage. As with fetchpriority, these resources can be images, fonts, scripts, and styles. You can also use this in conjunction with MIME types to give the browser more information about the resource being preloaded.

A very common use case is preloading web fonts in order to avoid a flash of unstyled text, or FOUT. (FOUT occurs when text is displayed using system fonts and then shifts or “flashes” when any web fonts are finally downloaded and applied.)

<link rel="preload" as="font" type="font/woff2" href="foo.woff2" crossorigin>

<link rel="preload" as="font" type="font/woff2" href="bar.woff2" crossorigin>

This code tells the browser to preload those two web font files, i.e., download them as early as possible so that they’re immediately available and won’t block the page’s render. The as and type attributes tell the browser that the resources being downloaded are, indeed, web fonts, while the crossorigin attribute is there to deal with any potential CORS issues.

As with fetchpriority="high", you should be very selective with what you preload. Attempting to preload too many resources will have have diminishing returns. For example, I use several web fonts here on Opus. However, I only preload one web font — Alegreya — because that’s what’s used for the vast majority of the website’s text.

Note: Preloading and fetchpriority are not panaceas, and should usually be implemented after you’ve already implemented some of the earlier methods. For example, if you’re using a massive PNG photo for a hero image, fetchpriority="high" won’t be as effective as taking the time to optimize the image first (e.g., converting it to WebP, resizing/cropping it, running it through compression).

D) Prefetch Webpages To Improve Loadtimes

Just as it’s possible to preload webpage resources before they’re used, it’s also possible to prefetch entire webpages before they’re visited. Prefetching a webpage loads it into the browser’s cache so that if/when a user does go there, it loads instantly.

There are two ways to prefetch webpages. The first is via a <link> tag, like so:

<link rel="prefetch" href="foo.html">

<link rel="prefetch" href="bar.html">

This approach works well if you’re fairly confident about which webpage the user will go to next. For example, if the user is filling out a form, then you might prefetch the confirmation page that’ll be displayed after the form is submitted. As with preloading, you don’t want to prefetch too many webpages, as that’ll force the browser to download and cache a bunch of stuff that’ll likely never be viewed.

Most of the time, however, it’s difficult to know with any real certainty where a user will go next. This is particularly true on something like a blog, where each post might have numerous links to other posts. You don’t want to prefetch them all, which will just be a waste of time and resources. This is where the Speculation Rules API comes in handy.

Here’s an example of what a simple speculation rule looks like.

<script type="speculationrules">
{
    "prerender": [{
        "where": {
            "href_matches": "/*"
        },
        "eagerness": "moderate"
    }]
}
</script>

After that code’s been added to your website’s <head>, it tells the browser to “prerender” any link that points to another page on your website. This happens behind the scenes, like it’s being loaded in another tab, so that if the user does click on a link, the new webpage appears instantly, as if by magic. (I don’t say that lightly. The first time I experienced speculation rules in action was pretty mind-blowing.)

Downloading and prerendering all of those webpages could get potentially burdensome on the browser, which is why speculation rules come with a couple of safeguards:

  1. Prerendering does not happen automatically, but only if a user hovers over a link for 200 milliseconds.
  2. Browsers will only keep a certain number of prerendered webpages in memory at any given point in time. As the user hovers over links, previously prerendered webpages are purged from memory in order to make room for newer ones.

Speculation rules are awesome, but they can have an unforeseen impact on website analytics. Since the browser is, in fact, downloading webpages — albeit behind the scenes — your website analytics tool might record those as hits even if the user never clicks on the links and actually visits the webpages in question. To prevent such false positives, it can be necessary to implement custom code that triggers your analytics tool only when a valid hit occurs.

Finally, speculation rules are currently only compatible with Chrome, Edge, and Opera; Firefox and Safari users won’t benefit from the instant navigation. Mind you, that’s not a reason to not implement speculation rules. Given Chrome’s dominance of the browser market, chances are most of your visitors will benefit from them. In the meantime, let’s hope that Firefox and Safari implement speculation rules in the not-too-distant future.


Every year, web browsers get more powerful while internet connections get faster. Both of which are very good things. But web developers should not see such advancements as an excuse to give up on squeezing every possible bit of performance from their websites.

That can certainly involve complex steps like server tuning and implementing CDN layers. But don’t overlook little things like optimizing images, minifying code, using native lazy loading, and implementing speculation rules. I think a lot of web developers and content managers will be very pleasantly surprised by the web performance gains that can be achieved with relatively minimal time and effort.

Enjoy reading Opus? Want to support my writing? Become a subscriber for just $5/month or $50/year.
Subscribe Today
Return to the Opus homepage