2021 Website Speed Checklist
Let’s make 2021... fast! This checklist for optimizing frontend website speed, translated into Czech, contains nearly everything you need to know to ensure a swift user experience today – from metrics and tools to developer techniques.
This checklist is a translation of the Front-End Performance Checklist 2021. Thanks to Smashing Magazine.
Article Content:
- Preparation: Planning and Metrics
- Setting Realistic Goals
- Defining the Development Environment
- File Optimization
- Build Optimization
- Loading Optimization
- Network and HTTP/2
- Testing and Monitoring
- Quick Wins
Preparation: Planning and Metrics
-
Create a "performance culture" that fosters website speed.
Website speed cannot be sustainable without investment. Study common complaints coming to customer support and see how increasing speed can help alleviate some of these problems. Create a case study with real data and business metrics tailored to your company. Plan your website's loading sequence and any potential compromises from the outset.
-
Aim to be 20% faster than your fastest competitor.
Gather data on the devices your website visitors are using. Prioritize real devices over simulations. Choose a mobile Moto G4/G5 Plus; a mid-range Samsung device (Galaxy A50, S8); a good mid-range device like Nexus 5X, Xiaomi Mi A3, or Xiaomi Redmi Note 7, and a slow device like Alcatel 1X or Cubot X19. Alternatively, emulate a mobile OS on a computer by testing on a limited network speed (e.g., 300 ms RTT, 1.6 Mb/s download, 0.8 Mb/s upload) with limited CPU (5× slowdown). Then switch to regular 3G, slow 4G (e.g., 170 ms RTT, 9 Mb/s download, 9 Mb/s upload), and Wi-Fi. Collect data, create a table, reduce the results by 20%, and set your goals ("performance budgets", speed limits).
-
Choose the right metrics.
Not every metric holds the same value from an optimization perspective. Study which metrics matter most: they will usually relate to how quickly the most important pixels begin to render and how fast the input response is. Focus on loading the page according to your visitors’ priorities. Typically, the focus is on Time To Interactive (TTI), First Input Delay (FID), rendering the "hero" element, Largest Contentful Paint (LCP), Total Blocking Time (TBT), and Cumulative Layout Shift (CLS). Not on: First Meaningful Paint.
-
Set testing profiles: "clean" and "customer".
Disable antivirus programs and CPU-intensive background tasks, stop background data transfers, and test with a "clean" user profile without browser extensions to avoid skewing results. Study which extensions your customers use and also test a special "customer" profile.
-
Share the Website Speed Checklist with your colleagues.
Ensure every team member knows this Website Speed Checklist. Every decision impacts performance, and your project will benefit significantly if ownership is distributed across the entire team. Simultaneously map design decisions against performance budgets.
Setting Realistic Goals
-
100 milliseconds response time, 60 frames per second.
Each animation frame should be executed within 16 milliseconds — ideally 10 milliseconds, achieving the desired 60 frames per second (1 second ÷ 60 = 16.6 milliseconds). Be optimistic and use idle time wisely; for demanding tasks like animations, it’s better to do nothing else on the page. Where it’s not possible, do only the bare minimum. The estimated input latency should be below 50 ms. Utilize idle time with the Idle Until Urgent approach.
-
LCP < 2.5 seconds; FID < 100 ms; CLS < 0.1; TTI < 5 seconds on 3G.
Considering a baseline device, an Android phone costing around £150 on a slow 3G network, emulated at a transfer speed of 400 ms RTT and 400 kB/s, aim for Time To Interactive (TTI) < 5 seconds, and 2–3 seconds for repeat visits. Focus on Largest Contentful Paint < 2.5 seconds and minimize Total Blocking Time (TBT) and Cumulative Layout Shift (CLS). Strive to reduce these values to a minimum.
-
File size cap < 170 kB.
The first 14–16 kB of HTML code is most critical — it’s the only part delivered in the first network roundtrip. To achieve the above goals, work with a file size of maximum gzipped size 170 kB (0.7 – 0.8 MB uncompressed). Ensure your speed limits change according to network conditions and hardware constraints.
Defining the Development Environment
-
Choose and configure your build tools wisely.
Don’t fret too much about whether it’s a "cool" tool or not. If you achieve results quickly and have no issues maintaining your build process, then all is well. Exceptions might be Webpack or Parcel, which provide useful optimization techniques like code-splitting. If you’re not using these techniques yet, then check out the aforementioned code-splitting and tree-shaking.
-
Use progressive enhancement by default.
First design and create a basic interface to meet the fundamental function of the web. Then enhance the interface with advanced features for different browsers, creating a robust and stable interface for various users with different devices. If your web runs fast on a slow device, with a poor screen and in a poor browser on an unoptimized network, then it will run faster on a fast device, with a good browser and on a decently fast network.
-
Set the performance bar high.
JavaScript entails the largest costs for user experience. With a 170 kB limit, which already includes critical data and paths – HTML / CSS / JavaScript, router, state management, tools, interfaces, and applications – thoroughly examine the time costs for network transfer, parsing, compilation, and runtime costs of the framework.
-
Evaluate every framework and every dependency.
Not every project needs a framework, not every single-page application (SPA) needs to load a framework. Choose wisely; evaluate third-party JS by examining features, accessibility, stability, speed, package ecosystems, learning curve, documentation, tools, records, team, compatibility, and security. Next.js (React), Gatsby (React), Vuepress (Vue), and Preact CLI provide decent defaults for fast loading on average mobile hardware.
-
Choose wisely: React, Vue, Angular, Ember, and the like.
Ensure the framework you choose offers server-side rendering (SSR) or pre-rendering. Before deciding, measure rendering times on mobile devices both on the server and the client. It is important to know the "nuts and bolts" of the framework you’ll rely on. Look at the PRPL pattern and App Shell architecture. Good options are Preact, Inferno, Vue, Svelte, Alpine, Polymer.
-
Optimize your API performance.
An API can easily become a bottleneck when accessed by multiple services at once. Consider leveraging GraphQL, allowing you to run queries over a type system created over your data. Unlike REST, GraphQL can fetch all data in one query without downloading too much or too little, which often happens in REST architectures.
-
Trying out Google AMP or Facebook Instant Articles?
You can achieve good performance without these technologies, but AMP offers a very strong performance foundation and global CDN. Instant Articles help primarily with visibility on Facebook and can load up to 4 times faster than regular mobile websites.
-
Choose your CDN wisely.
Consider how much you rely on dynamic data. You can at least partially generate your content statically, upload it to a CDN and thereby avoid unnecessary database queries (JAMStack). Check that the CDN performs content compression and image conversion (e.g., optimization and resizing or format conversion) and supports service workers.
File Optimization
-
Use Brotli for compression.
Brotli is a new lossless data format, supported in all modern browsers. It’s more efficient than GZip and Deflate. Compression is very slow, but decompression is fast. Pre-compress static assets using Brotli and Gzip at the highest level, dynamically compress HTML at runtime with Brotli (level 1-4). Also check Brotli support on your CDN. Ensure the server handles “content negotiation” for Brotli or Gzip correctly.
-
Use responsive images, AVIF, and WebP.
Where possible, use responsive images with
srcset, sizes, <picture> and image-setattributes. Use AVIF and WebP formats with<picture>and JPEG/PNG as fallback, or detect support via the Accept header. Note: with WebP you reduce data volume, but with JPEG you can improve perceived speed thanks to the "progressive rendering" feature. -
Are images properly optimized?
Use mozJPEG for JPEG compression, SVGO for SVG compression, Pingo for PNG, or Squoosh for all of them (including AVIF). To check the efficiency of your responsive markup, you can use the imaging-heap tool. For critical images, use progressive JPEG and blur unnecessary parts (applying Gaussian blur) and remove contrast (you can reset it using CSS filters). Ensure you have width and height attributes set for all your images. Add automatic image compression to your pull requests and explore lazy loading of less critical images.
-
Are videos properly optimized?
Instead of animated GIFs, use either animated WebP (with GIF as fallback) or embed loops with inline HTML5 video. Ensure your MP4 files are processed with multipass encoding, blurred using the “frei0r iirblur” effect (if available), moov atom metadata is moved to the file header, and your server accepts Byte serving. Whenever possible, use video in AV1 format but also provide a fallback for older formats. Adjust video dimensions and use adaptive media delivery to provide visitors with content they can play on their devices without delay.
-
Are fonts optimized?
Set the correct subsetting for fonts. Prioritize WOFF2 and use WOFF as fallback. Display content immediately with a fallback font, load the custom font asynchronously and switch to it only afterward. Ideal solution: two-stage rendering, first with a small supersubset and load the rest asynchronously later. Preload 1-2 fonts from each family. Avoid using local() in font declarations, but consider using fonts installed in the OS. Don’t forget to use font-display:optional and use Font Load events for redrawing. Use Web Font Reflow Count and Time To Real Italics metrics.
Build Optimization
-
Set priorities correctly.
Check all your resources (JavaScript, images, fonts, third-party scripts, "expensive" modules on the page) and divide them into groups. Define the basic experience (fully accessible basic content for older browsers), enhanced experience (enriched, full-featured for modern browsers), and extras (resources that are not strictly required and can be lazy-loaded).
-
Use native JavaScript modules in production.
In older browsers, support the basic experience, full-featured only in modern browsers. For loading JavaScript use ES2017+
<script type="module">. Modern browsers interpret the script as a JavaScript module and execute it as expected. Older browsers ignore it. -
Running JavaScript is expensive, so tame it.
In SPA applications, you need some time to initialize the app before you can render the page. Look for modules and techniques to speed up initial rendering times, such as progressive hydration and importing on interaction (on older mobiles, times are 2-5x higher).
-
Use tree-shaking, scope hoisting, and code-splitting to reduce loading.
Tree-shaking is a way that only uses the code during the build process that is actually used. Code-splitting divides code into "chunks" loaded on demand. Scope hoisting detects where chain imports can be merged and converted into an inline function while maintaining functionality (e.g., using Webpack). Use granular chunking and move some of the rendering from the client to the server. Define splitting by monitoring which CSS/JS blocks are used and which are not. Also consider code-splitting at the package level.
-
Can you move JavaScript to a Web Worker or WebAssembly?
As code evolves, performance bottlenecks in the user interface start to appear. It often happens that DOM operations run alongside your JS in the main thread. Consider moving these expensive operations to background processes that run in another thread using web workers. A typical case: preloading data into a PWA. Consider moving computationally intensive tasks to WebAssembly, which works best for demanding web applications, such as games.
-
Send code for older browsers only to those older browsers.
Use babel-preset-env to transpile ES2017+ features not supported by modern browsers you support. Then set up two build versions, one for modern and the other for older browsers.
For lodash, use babel-plugin-lodash, which only loads the modules you use in the source code. Change the general lodash require and select only the ones used to prevent code duplication. Use
<link rel="modulepreload">to initiate early (high priority) module loading. -
Identify and rewrite legacy code using "incremental decoupling".
Go through project dependencies and assess how much time would be needed to refactor or rewrite legacy code. First, set metrics that monitor whether the ratio of legacy code calls remains the same or decreases. The ratio must not increase. Set conditions in the team that do not tolerate the use of additional libraries and ensure CI always alerts developers upon pull requests.
-
Identify and remove unused CSS/JavaScript.
CSS and JavaScript coverage in Chrome browser allows you to see which code was executed/used and which was not. Once unused code is identified, find these modules and add lazy loading using import(). After adjusting, test code coverage again and verify that less code is now sent upon first load. Use Puppeteer to automatically gather code coverage results.
-
Reduce the size of JavaScript dependencies.
There's a good chance you have entire JavaScript libraries in your projects, but you’re only using a fraction of the code. Consider using automatic webpack-libs-optimizations, which removes unused methods and polyfills during the build process. Add package control to your regular workflow. Bundlephobia helps quantify the performance impact of adding individual NPM packages to a project. Size-limit extends the classic package size check with the execution time in JavaScript. Skypack can be used as a source for quality- and performance-oriented packages managed by individual community members.
-
Are you using predictive prefetch for JavaScript chunks?
Use your experience and intuition to determine when to prefetch your JS. Guess.js includes tools that use Google Analytics data to determine which page the user is likely to visit next. Consider Quicklink, Instant.page, and DNStradamus. Note: You might be instructing the browser to download unnecessary data and prefetch unnecessary pages. We definitely recommend caution regarding the number of preloaded requests.
-
Optimize for your target JavaScript engines.
Use script streaming for monolithic scripts so they can be parsed as soon as downloading begins in a separate thread in the background. Also, employ code caching V8 engine by separating libraries from the code that uses them. Consider also JIT optimization strategies for Baseline Interpreter in Firefox browser.
-
Find a way to combine client-side and server-side rendering.
Typically, the goal is to find the optimal balance between client-side and server-side rendering. Consider pre-rendering if your pages don’t change too often and, if possible, defer framework bootstrapping. Stream HTML chunks using server-side rendering and hydrate only on display, interaction, or during idle to get the best of both worlds. (Streaming Server-Side Rendering With Progressive Hydration).
-
Consider micro-optimizations and progressive bootstrapping.
Use server-side render for fast “first meaningful paint” (FMP), but also include minimal JS to keep “time to interactivity” (TTI) close to the “first meaningful paint”. Then either on-demand or as time permits, execute non-essential parts of the application. Always split function execution into asynchronous tasks. Where possible, use requestIdleCallback.
-
Host third-party resources yourself.
Using a public CDN isn’t automatically faster. Even if two sites refer to the exact same URL of a third-party resource, the code is downloaded again for each domain. The cache is isolated per domain. Own resources are more likely to remain cached than third-party resources. Self-hosting is more reliable, secure, and performant.
-
Limit the impact of third-party scripts.
Too often, a single third-party script makes the "long tail" and ruins speed. Consider using service workers to fetch resources with a timeout. Set a Content Security Policy (CSP) to limit the impact of third-party scripts, such as disabling audio or video downloads. Embed scripts via iframe, so they don’t have access to the DOM. For load testing scripts, explore the summary on the Performance tab (DevTools). Load third-party scripts after the application has loaded. Focus on code snippets that prevent content flashing before the script loads.
-
Set HTTP cache headers correctly.
Check that expires, cache-control, max-age, and other HTTP cache headers are set correctly. Generally, resources should be cacheable either for a very short time (if they are likely to change) or indefinitely (if they are static).
Use cache-control: immutable to prevent revalidation. Check you’re not sending unnecessary headers (e.g., x-powered-by, pragma, x-ua-compatible, expires). Utilize zero RTT for repeat view through stale-while-revalidate.
Loading Optimization
-
Load JavaScript asynchronously.
Use lazy loading for all components such as large JS files, videos, iframes, widgets, and images. Use native lazy-loading (loading and importance attributes), or a solution using Intersection Observer. The latter can also be used for powerful "scrollytelling" - parallax-type effect, or ad tracking.
-
Defer rendering and decoding of large images.
With the content-visibility: auto property, we can instruct the browser to skip rendering the descendants of an element if the element is outside the viewport. Ensure you use the contain-intrinsic-size property with a suitably chosen placeholder, to prevent deteriorating the Cumulative Layout Shift (CLS) metric. Also use
<img decoding="async">. The browser will then decode the image off the main thread, reducing the CPU time needed for the operation. -
Load critical CSS as quickly as possible.
Extract all styles needed to render content visible on the page in the viewport (so-called critical CSS or above-the-fold CSS) from CSS. Add these styles inline into the
<head>tag on the page. The golden rule is you shouldn't exceed a total size of 14 kB. Consider conditionally embedding CSS on the page. In some cases, it is more beneficial to place critical CSS into a separate file in the domain root. Thanks to caching, this solution may be better than embedding inline styles. -
Experiment with reordering CSS files/properties.
When optimizing load, consider splitting the main CSS by individual media queries. Don’t place
<link rel ="stylesheet"/>before asynchronous files. If scripts don’t depend on styles, consider placing blocking scripts above blocking styles. If they do, split this JavaScript into two and load it before and after CSS files. Cache inline CSS using service workers and experiment with in-body CSS. Dynamic styles can be “expensive”: check if CSS-in-JS is optimizing execution, if CSS has no dependencies on appearance or component properties, don’t complicate components (styled-components). -
Stream responses.
Streaming provides an interface for reading or writing asynchronous chunks of data, of which a subset may be available in memory at any given time. Instead of serving an empty UI skeleton and filling content with JavaScript, use a service worker, load the skeleton from cache, and content from the network. HTML rendered during the initial request can then fully take advantage of the browser's HTML parser.
-
Consider adapting your components to connection and device memory.
Use the client-hint header Save-Data and adapt the application by limiting your users (cost, performance). You can override high-DPI image requests to low-DPI, remove web fonts, parallax, disable autoplay, or even change how you deliver code. Use the Network Information API to deliver variants of demanding components based on connection and the Device Memory API to adapt resources based on device memory.
-
Maintain persistent connections to speed up delivery.
Save time using resource hints; dns-prefetch (background DNS lookup), preconnect (initiate connection handshake (DNS, TCP, TLS)), prefetch (resource request), preload (preload resources without executing them) and prerender (load resources in advance without executing JS and without rendering on the page). When using preload, as must be defined, or nothing will load. Preloading fonts without the crossorigin attribute will cause double loading. When using preload, there are a number of priorities, so consider embedding rel="preload" elements in the DOM just before external blocking scripts.
-
Use service workers for caching (caching) and as a fallback during network outages.
If your web runs on HTTPS, cache static assets in service worker cache (caching) and store offline fallback (or even entire pages) and load them from the device instead of the network. Cache the application environment in the service worker cache along with several critical pages, e.g., offline page or homepage. However, ensure there is a correct CORS response header, do not cache opaque responses, and register cross-origin images in CORS mode.
-
Use service worker on CDN/Edge (e.g., for A/B testing).
For CDNs implementing service workers on the server, consider debugging performance using service workers directly "on the edge". For example, in A/B tests, when HTML needs to change its content for different users, use the service worker on CDN to process the logic.
Speed up sites using Google Fonts by using stream HTML rewriting.
-
Optimize rendering performance.
If necessary, isolate expensive components using CSS containment. Ensure there is no delay when scrolling the page or animating an element, and that you consistently achieve 60 frames per second. If it’s not possible, try to keep the frame rate at least constant. The minimum redraw rate is generally considered to be 15 frames per second. Use CSS will-change to inform the browser about which elements will change.
-
Have you optimized the rendering experience?
Don’t underestimate the role of perceived speed. If you’re loading files, always try to be a step ahead of the customer so the experience is smooth, even if a lot is happening in the background. To engage the customer, use "skeletons" instead of loading spinners and add transitions and animations.
-
Prevent layout shifts (reflow) and repaints.
Layout shifts are commonly caused by resizing components such as videos and images, lazy-loading web fonts, embedded ads, or lazy-loading real content into components. Set width and height for images so modern browsers can reserve the necessary space. Use placeholder SVGs to reserve enough space where images or videos will appear. Use JavaScript lazy-loading if a native version is unavailable for the component. Download JS lazy-loading only when you’ll actually use it. Identify layout shifts caused by web fonts and match line-height and spacing using font-style-matcher. Monitor layout stability with the Layout Instability API and Cumulative Layout Shift (CLS) metric.
Network and HTTP/2
-
Is OCSP stapling enabled?
OCSP stapling can speed up the TLS authentication process because the browser doesn’t have to spend time looking up and downloading information about your domain’s certificate.
-
Have you reduced the impact of SSL certificate revocation?
Extended Validation (EV) certificates are expensive and time-consuming as they always require human verification. Ordinary Domain Validation (DV) certificates are available for free (Let’s Encrypt) and their issuance and renewal can easily be automated. EV certificates also don’t support OCSP stapling, so always have DV certificates with stapling enabled.
-
Are you using IPv6?
Studies show that websites are up to 15% faster thanks to NDP (Neighbor Discovery Protocol) and route optimization. Update your DNS records so your site is accessible via IPv6. However, don’t forget about existing IPv4, as the two versions are not compatible with each other.
-
Is TCP BBR being used?
BBR is a relatively new TCP flow control algorithm. It responds to actual congestion rather than packet loss, as TCP does. As such, it is significantly faster, with higher throughput and lower latency. Enable BBR and set tcp_notsent_lowat to 16 kB to ensure HTTP/2 prioritization works reliably on Linux kernel 4.9 and newer.
-
Always prioritize HTTP/2.
HTTP/2 is very well supported and offers speed improvements. Depending on how large your mobile user base is, you may need to send different builds (application assemblies) to users and you can benefit from customizing different builds for different devices. (HTTP/2 is often slower on networks with noticeable packet loss.)
-
Deploy HTTP/2 properly.
You must find a delicate balance between combining modules into one and parallel loading of many small modules. Break your entire interface into many small modules; then group them, compress them, and make bundles out of them. Split them at the package level or by monitoring which CSS/JS blocks are unused. Separate third-party code from your own and separate dependencies that rarely change from those that often do. Sending approximately 6–10 frontend element bundles seems like a decent compromise (and it’s not bad for older browsers). Experiment and measure to find the right balance. Try to send as many elements as possible over a single HTTP/2 connection.
-
Do your servers and CDN support HTTP/2?
Different servers and CDNs support HTTP/2 differently. Using tools that compare CDNs (e.g., CDN Comparison), you can check your options or quickly look up the level of support for different features. Enable BBR, set tcp_notsent_lowat to 16 kB for HTTP/2 prioritization.
-
Is HPACK compression used?
If you’re using HTTP/2, check that your servers implement HPACK compression for HTTP response headers to reduce unnecessary overhead. HTTP/2 servers are relatively new and may therefore not fully support the specification, an example being HPACK. H2spec is a great (though technically overly detailed) tool to check this.
-
Prepare for HTTP/3.
On HTTP/2, connections are shared by multiple requests. On HTTP/3, requests also share a connection, but stream independently, so a dropped packet no longer affects all requests, only a single stream. With QUIC technology on HTTP/3, TCP and TLS are combined and performed in a single "round trip". From the second connection, we can already send and receive application-layer data in the first "round trip" (0-RTT). Packaging frontend elements still matters, so instead of sending monolithic JS, send multiple JS files in parallel, as on HTTP/2. Expect HTTP/3 to impact load times on mobile devices.
-
Do your servers and CDN support HTTP/3?
QUIC and HTTP/3 are better and more bulletproof: with faster "handshakes", better encryption, more reliable independent streams, more encrypted, and with 0-RTT if the client has previously connected to the server. However, it’s quite demanding on CPU (2–3 times CPU usage for the same bandwidth). Check if your servers or CDN support the HTTP over QUIC protocol (also known as HTTP/3) and enable it if you can.
-
Check if your server’s security is bulletproof.
Verify that security headers are set correctly, remove known security vulnerabilities, and check HTTPS settings. Ensure all external plugins and tracking scripts are loaded over HTTPS, cross-site scripting isn’t possible, and the HTTP Strict Transport Security headers and Content Security Policy headers are correctly set.
Testing and Monitoring
-
Monitor mixed-content warnings.
If you recently migrated from HTTP to HTTPS, don’t forget to monitor both active and passive mixed-content warnings using tools like Report-URI.io. You can also use Mixed Content Scan to scan for mixed content on your HTTPS-enabled site.
-
Have you optimized the audit and debugging workflow?
Invest time in studying debugging and auditing techniques in your debugger, WebPageTest, Lighthouse, and expand your text editor. For example, you can run WebPageTest from a Google sheet and integrate accessibility, performance, and SEO scores into your Travis setup using Lighthouse CI or directly into Webpack. For quick checks, use CSS Perf Diagnostic.
-
Have you tested in proxy browsers and older browsers?
Testing in Chrome and Firefox isn’t enough. Look at how your site works in proxy browsers and older browsers (including UC Browser and Opera Mini). Measure the average internet speed among your user base to avoid big surprises. Test with network throttling and emulate high-DPI devices. BrowserStack is fantastic, but also test on real devices.
-
Have you tested the speed of your 404 pages?
Every time a client requests a web element that doesn’t exist, they receive a 404 response — and this response is often huge. Don’t forget to explore and optimize the cache strategy also for your 404 pages. Ensure that the browser only displays HTML if it expects an HTML response, and return an error response for all other responses.
-
Have you tested the impact of GDPR consent prompts and cookie banners?
Normally, cookie consent prompts shouldn’t impact CLS, but sometimes they can, so consider using free and open-source resources like Osano or cookie-consent-box. Consent is likely to change script impact on overall performance, so set and study several different web performance testing profiles for different types of consent.
-
Have you tested the impact on accessibility?
Large pages and DOM manipulation with JavaScript cause delays in screen reader announcements. A fast Time to Interactive means how much time elapses before a screen reader can announce navigation to a given page and the screen reader user can actually press the keyboard for interaction.
-
Is continuous monitoring set up?
Good speed metrics arise from a combination of passive and active monitoring tools. A private WebPagetest instance and using Lighthouse are always advantageous for quick tests, but also setting up continuous monitoring using RUM tools like SpeedTracker, SpeedCurve, and others. Set your own user-timing marks for measuring and monitoring metrics specific to your web.
Quick Wins
The Website Speed Checklist is quite comprehensive, and completing all optimizations may take some time. So, if you had just 1 hour to achieve significant improvements, what would you do? Let’s boil it all down to 18 low-hanging fruits. Of course, before you start and once you finish, measure the results, including metrics like Largest Contentful Paint and Time To Interactive on 3G and also wired connections.
- Measure real user speed and set appropriate targets. Aim to be at least 20% faster than your fastest competitor. Keep Largest Contentful Paint under 2.5 seconds, First Input Delay below 100 ms, Time to Interactive below 5 seconds on slow 3G, and below 2 seconds for repeat visits. Optimize at least for First Contentful Paint and Time To Interactive.
- Optimize images using Squoosh, mozjpeg, guetzli, pingo and SVGOMG and serve AVIF/WebP with an image CDN.
- Prepare critical CSS for your main templates and inline it into
<head>of each. For CSS and JS, keep a budget for critical file sizes of max 170 kB gzipped (0.7 MB unzipped). - Trim, optimize, defer, and lazy-load scripts. Invest in configuring your bundler to eliminate redundancy and look for less data-heavy alternatives.
- Always host static frontend files on your own domain. Prefer also hosting third-party components on your own domain. Limit their impact. Use facades and placeholders, load widgets on interaction, and watch out for flicker of the original version.
- Be discerning when choosing a framework. For single-page applications (SPA), identify critical pages and render them statically, or at least pre-render them. Use progressive hydration at the component level and import modules on interaction.
- Client-side rendering alone isn’t a good performance choice. Pre-render if your pages don’t change much, and defer framework bootstrapping. Use streamed server-side rendering (SSR).
- Serve legacy code only to older browsers using the module/nomodule pattern.
- Experiment with reordering CSS rules and test CSS served directly in HTML.
- Add resource hints to speed up serving with dns-lookup, preconnect, prefetch, preload, prerender.
- Subset web fonts and load them asynchronously, use font-display in CSS for fast initial rendering.
- Check if HTTP caches and security headers are set correctly.
- Enable Brotli compression on the server. (If not possible, remember to enable Gzip compression.)
- Enable TCP flow control using BBR if your server is running on Linux kernel version 4.9+.
- If possible, enable OCSP stapling and IPv6. Always serve a DV certificate with OCSP.
- Enable HPACK compression for HTTP/2 and migrate to HTTP/3 if available.
- Cache elements like fonts, styles, JavaScript, and images in the Service Worker cache.
- Explore options to avoid rehydration, use progressive hydration and server-side streaming for your single-page application (SPA).
Special thanks for reviewing the original article on Smashing Magazine go to Guy Podjarny, Yoav Weiss, Addy Osmani, Artem Denysov, Denys Mishunov, Ilya Pukhalski, Jeremy Wagner, Colin Bendell, Mark Zeman, Patrick Meenan, Leonardo Losoviz, Andy Davies, Rachel Andrew, Anselm Hannemann, Barry Pollard, Patrick Hamann, Gideon Pyzer, Andy Davies, Maria Prosvernina, Tim Kadlec, Rey Bango, Matthias Ott, Peter Bowyer, Phil Walton, Mariana Peralta, Pepijn Senders, Mark Nottingham, Jean Pierre Vincent, Philipp Tellis, Ryan Townsend, Ingrid Bergman, Mohamed Hussain S. H., Jacob Groß, Tim Swalling, Bob Visser, Kev Adamson, Adir Amsalem, Aleksey Kulikov, and Rodney Rehm.
Czech translation: Tomáš Hejč, Martin Brychta, Zuzana Fatrdla, Martin Michálek. (PageSpeed.ONE Team)
Stay informed about your website's speed.
Subscribe to our newsletter. Each month, we share the latest in website speed for site owners, marketers, and developers.
Tags:Web Performance