Web Speed Checklist 2021
Let's make 2021... fast! In this English translation, we present a checklist for optimizing frontend web speed. It encompasses nearly everything you need to know to provide a swift user experience on the web today – from metrics and tools to development techniques.
This checklist is a translation of the Front-End Performance Checklist 2021. Thanks to Smashing Magazine.
Contents:
- 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 supports web speed.
Sustaining web speed long-term requires investment. Study common complaints received by customer support and determine how increasing speed can alleviate some of these issues. Craft a case study using real data and business metrics tailored to your company. Plan your website’s loading strategy and potential trade-offs from the very start.
-
Aim to be 20% faster than your fastest competitor.
Gather data on the devices your web visitors use, prioritizing actual devices over simulations. Choose devices like Moto G4/G5 Plus; mid-range Samsung devices (Galaxy A50, S8); solid mid-range devices like Nexus 5X, Xiaomi Mi A3, or Xiaomi Redmi Note 7, and slow devices like Alcatel 1X or Cubot X19. Alternatively, emulate mobile OS on a computer by testing on limited network speeds (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, compile a table, reduce results by 20%, and set your goals with “performance budgets”.
-
Select the right metrics.
Not all metrics hold the same value from an optimization perspective. Study which metrics matter most: they typically relate to how quickly the most important pixels begin to render and how swift the input response is. Focus on page loading according to your visitors' priorities. Usually, it is about Time To Interactive (TTI), First Input Delay (FID), hero element rendering time, Largest Contentful Paint (LCP), Total Blocking Time (TBT), and Cumulative Layout Shift (CLS). Not on: First Meaningful Paint.
-
Set up test profiles: “clean” and “customer.”
Disable antivirus programs and CPU-intensive background tasks, halt background data transfers, and test with a “clean” user profile without browser extensions to prevent skewed results. Study which extensions your customers use and test a special “customer” profile as well.
-
Share the Web Speed Checklist with your colleagues.
Ensure every team member is familiar with the Web Speed Checklist. Every decision impacts performance, and distributing ownership across the team significantly benefits your project. Simultaneously map design decisions against the performance budget.
Setting Realistic Goals
-
100-millisecond response time, 60 frames per second.
Every animation frame should execute within 16 milliseconds — ideally 10 milliseconds, achieving the desired 60 frames per second (1 second ÷ 60 = 16.6 milliseconds). Be optimistic and wisely utilize idle time; for demanding tasks like animations, it’s better not to do anything else on the page. Where it’s unavoidable, do only the bare minimum. Estimated input latency should be less than 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 as an Android phone costing around 160 EUR on a slow 3G network, emulated at 400 ms RTT and 400 kB/s transfer speed, aim for a Time To Interactive (TTI) < 5 seconds and repeat visits below 2–3 seconds. 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 ceiling < 170 kB.
The first 14–16 kB of HTML code is critical — it’s the only part delivered in the first network “round trip.” To achieve the aforementioned goals, work with file sizes capped at a maximum gzipped size of 170 kB (0.7 – 0.8 MB decompressed). Ensure your speed limits adjust according to network conditions and hardware constraints.
Defining the Development Environment
-
Choose and configure your build tools wisely.
Don’t overly concern yourself with whether a tool is “cool” or not. If you achieve results quickly and maintain your build process without issues, you’re doing fine. Exceptions might be Webpack or Parcel, which offer useful optimization techniques like code-splitting. If you haven’t used these yet, check out code-splitting and tree-shaking.
-
Use progressive enhancement by default.
Start by designing and developing a basic interface to fulfill the core function. Then enhance the interface with advanced features for different browsers, creating a resilient and stable interface for users with various devices. If your site runs well on a slow device, with a poor screen, and in a suboptimal browser on a bad network, it will run faster on a good device, with a good browser, and on a good network.
-
Set high performance standards.
JavaScript entails the highest costs for user experience. With a 170 kB limit that already includes critical data and paths – HTML / CSS / JavaScript, router, state management, tools, interface, and app – carefully examine the time costs of network transfer, parsing, compilation, and runtime framework costs.
-
Evaluate every framework and dependency.
Not every project needs a framework, and 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 quick loading on average mobile hardware.
-
Choose wisely: React, Vue, Angular, Ember, and company.
Ensure your chosen framework provides server-side rendering (SSR) or prerendering. Measure rendering times on both server and client on mobile devices before deciding. It’s essential to know the “nuts and bolts” of the framework you rely on. Look into the PRPL pattern and App Shell architecture. Good options include Preact, Inferno, Vue, Svelte, Alpine, Polymer.
-
Optimize your API performance.
APIs can easily become bottlenecks when accessed by multiple services simultaneously. Consider leveraging GraphQL, which allows you to query a type system you create over your data. Unlike REST, GraphQL can fetch all data in one request without over-fetching or under-fetching, common in REST architectures.
-
Considering Google AMP or Facebook Instant Articles?
You can achieve good performance without these technologies, but AMP offers a solid performance foundation and global CDN. Instant Articles help gain visibility on Facebook with up to 4x faster loading than regular mobile sites.
-
Choose your CDN wisely.
Consider how dependent you are on dynamic data. You can at least generate some of your content statically, upload it to a CDN, and avoid unnecessary database queries (JAMStack). Check that your CDN performs content compression and image conversion (e.g., optimization and resizing or format change) and supports server workers.
File Optimization
-
Use Brotli for compression.
Brotli is a new lossless data format supported in all modern browsers. It is more efficient than GZip and Deflate. Compression is very slow, but decompression is fast. Precompress static assets with Brotli and Gzip at the highest level; compress HTML dynamically on the fly using 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.
Use responsive images with
srcset, sizes, <picture>, and image-setattributes wherever possible. Utilize AVIF and WebP formats with<picture>and JPEG/PNG as fallback or detect support via the Accept header. Note: WebP reduces data size, but JPEG can improve perceived speed through “progressive rendering.” -
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 effectiveness of your responsive markup, use the imaging-heap tool. For critical images, use progressive JPEG and blur unnecessary parts (apply Gaussian blur) and remove contrast (you can adjust it again using CSS filters). Ensure width and height attributes are set for all your images. Add automatic image compression to your pull requests and explore lazy loading for 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 undergo multipass-encoding, are blurred using the “frei0r iirblur” effect (if available), moov atom metadata is moved to the header of the file, and your server supports Byte serving. Use AV1 format video whenever possible, but provide fallback for older formats. Adjust video dimensions and use adaptive media delivery to provide content visitors can play on their devices without delay.
-
Are fonts optimized?
Set the correct subsetting for fonts. Prefer WOFF2 and use WOFF as a fallback. Display content immediately using a fallback font, load the custom font asynchronously, and switch the font 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 employ Font Load events for redrawing. Use metrics like Web Font Reflow Count and Time To Real Italics.
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 a baseline experience (fully accessible baseline content for older browsers), enhanced experience (enriched, full-fledged for modern browsers), and extras (resources not strictly required and can be lazy-loaded).
-
Use native JavaScript modules in production.
Support the baseline experience in older browsers, full-fledged only in modern browsers. Use ES2017+
<script type="module">for loading JavaScript. 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.
SPA applications require some time to initialize the app before you can render the page. Look for modules and techniques to speed up initial rendering time, 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 to use only the code that is actually used during the build. Code-splitting divides code into “chunks” that are loaded on demand. Scope hoisting detects where chains of imports can be served and converted into inline functions while preserving functionality (e.g., using Webpack). Use granular chunking and move some rendering from client to server. Define splitting by monitoring which CSS/JS chunks 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 running in a separate thread using web workers. A typical case: preloading data into PWA. Consider moving compute-intensive tasks to WebAssembly, which works best for demanding web applications like games.
-
Send old code to old browsers only.
Use babel-preset-env to transpile ES2017+ features unsupported by modern browsers you support. Then set up two builds, one for modern and one for older browsers.
For lodash, use babel-plugin-lodash to load only the modules you use in your source code. Change general lodash require and select only the ones used to prevent code duplication. Use
<link rel="modulepreload">to initiate early (high-priority) loading of modules. -
Identify and rewrite legacy code using “incremental decoupling.”
Review project dependencies and assess how much time would be needed to refactor or rewrite old code. First, set metrics to track whether the ratio of legacy code calls remains the same or decreases. The ratio must not increase. Set team conditions that do not tolerate using additional libraries and ensure CI always alerts developers during pull requests.
-
Identify and remove unused CSS/JavaScript.
CSS and JavaScript coverage in Chrome allows you to see which code has been executed/used and which hasn’t. Once you identify unused code, find these modules and add lazy loading using import(). After adjusting, repeat the coverage test and verify that less code is sent on the first load. Use Puppeteer to automatically gather code coverage results.
-
Reduce JavaScript dependency size.
There’s a good chance you have entire JavaScript libraries in your projects but actually use only a fraction of the code. Consider using automatic webpack-libs-optimizations, which removes unused methods and polyfills during the build process. Add package check to your regular workflow. Bundlephobia helps estimate the performance impact of adding individual NPM packages to a project. Size-limit extends the classic package size check with JavaScript execution time length. Skypack can be used as a source of packages focused on quality and performance, 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 data from Google Analytics to determine which page users are 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 strongly recommend being cautious about the number of prefetch requests.
-
Optimize for your target JavaScript engines.
Use script streaming for monolithic scripts so they can be parsed immediately after downloading in a separate background thread. Also, engage code caching V8 engine by separating libraries from the code that uses them. Also, consider JIT optimization strategies for the Firefox Baseline Interpreter.
-
Find a way to combine client-side and server-side rendering.
The goal is usually to find the optimal balance between client-side and server-side rendering. Consider prerendering if your pages don’t change too often, and if possible, delay framework booting. Stream HTML chunks using server-side rendering and hydrate only upon display, interaction, or idle time to get the best of both worlds. (Streaming Server-Side Rendering With Progressive Hydration).
-
Consider micro-optimizations and progressive booting.
Use server-side render for fast “first meaningful paint” (FMP), but also include minimal JS to keep “time to interactivity” (TTI) close to “first meaningful paint.” Then, either on demand or as time allows, run non-essential parts of the app. Always split function execution into asynchronous tasks. If possible, use requestIdleCallback.
-
Host third-party resources yourself.
Using a public CDN is not automatically faster. Even if two websites link to the exact same URL of a third-party resource, the code is downloaded again for each domain. Cache is isolated per domain. Self-hosted resources are more likely to stay cached than third-party ones. Self-hosting is more reliable, secure, and performant.
-
Limit the impact of third-party scripts.
Too often, a third-party script creates a “long tail” that spoils speed. Consider using service workers to download resources with a timeout. Set Content Security Policy (CSP) to limit the impact of third-party scripts, such as disabling audio or video downloads. Embed scripts via an iframe so they don’t have access to the DOM. For load testing scripts, explore the Performance tab (DevTools) summary. Load third-party scripts only after the app loads. Focus on code pieces that prevent flashing before the script loads.
-
Correctly set HTTP cache headers.
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. Ensure you’re not sending unnecessary headers (e.g., x-powered-by, pragma, x-ua-compatible, expires). Use zero RTT for repeat views 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. Utilize native lazy-loading (loading and importance attributes) or solutions using Intersection Observer. The latter can also be used for powerful “scrollytelling” - parallax effects, or ad tracking.
-
Defer rendering and decoding of large images.
With the content-visibility: auto property, we can instruct the browser to skip rendering descendants of an element if it’s outside the viewport. Ensure you use contain-intrinsic-size with a suitably chosen placeholder to prevent worsening the Cumulative Layout Shift (CLS) metric. Use
<img decoding="async">. The browser will then decode the image off the main thread, reducing CPU time needed for the operation. -
Load critical CSS as quickly as possible.
Extract all styles needed to render content visible on the page within the viewport (known as critical CSS or above-the-fold CSS). Add these styles inline within the
<head>tag on the page. The golden rule is not to exceed a total size of 14 kB. Consider conditional CSS loading on the page. In some cases, embedding critical CSS into a separate file in the domain root might be more beneficial due to caching than inline styles. -
Experiment with reshuffling CSS files/properties.
In loading optimization, you can also consider splitting main CSS according to individual media queries. Do not 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 embedded CSS via service workers and experiment with in-body CSS. Dynamic styles can be “expensive”: check if CSS-in-JS optimizes execution if CSS has no dependencies on the component’s appearance or properties, don’t complicate components (styled-components). -
Stream responses.
Streaming provides an interface for reading or writing asynchronous data chunks, whose subset may be available in memory at a given moment. 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 utilize 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 to users’ constraints (cost, performance). You can override high-DPI image requests to low-DPI, remove web fonts, parallax, disable autoplay of video, or even change how you deliver code. Use Network Information API to deliver variants of resource-intensive components based on connection and Device Memory API to adapt resources based on device memory.
-
Maintain persistent connections to speed up delivery.
Save time by using resource hints; dns-prefetch (background DNS lookup), preconnect (initiate connection handshake (DNS, TCP, TLS)), prefetch (resource request), preload (load resources without executing them), and prerender (load resources beforehand without executing JS or rendering on the page). With preload, as must be defined, or nothing will load. Preloading fonts without the crossorigin attribute leads to double loading. When using preload, there are several priorities, so consider embedding rel="preload" elements into the DOM just before external blocking scripts.
-
Use service workers for caching and as a fallback during network outages.
If your website runs on HTTPS, cache static assets in service worker cache and store offline fallback (or even entire pages) to load them from the device rather than the network. Cache the app environment in the service worker cache along with some 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 server service workers, consider performance debugging using service workers directly “on the edge.” For example, in A/B tests, when HTML needs to change its content for different users, use a CDN service worker to handle the logic.
Speed up websites using Google fonts with stream HTML rewriting.
-
Optimize rendering performance.
When necessary, isolate expensive components using CSS containment. Ensure no delay occurs while scrolling the page or animating an element and consistently achieve 60 frames per second. If this isn’t possible, strive to keep frame rates at least constant. The minimum frame redraw speed is generally considered 15 frames per second. Use CSS will-change to inform the browser which elements will change.
-
Do you have an optimized rendering experience?
Don’t underestimate the role of perceived speed. If you’re loading files, always try to be one step ahead of the customer, so the experience remains smooth even when many things are happening in the background. To engage customers, use “skeletons” instead of loading spinners and add transitions and animations.
-
Prevent layout shifts and repaints.
Layout shifts are commonly triggered by resizing components like videos and images, lazy loading of web fonts, embedded ads, or loading actual 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 isn’t available for the component. Download JS lazy-loading only when you’ll use it. Identify layout shifts caused by web fonts and compare line-height and spaces using font-style-matcher. Track layout stability using the Layout Instability API and the 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 need to spend time looking up and downloading your domain’s certificate information.
-
Have you reduced the impact of SSL certificate revocation?
Extended Validation (EV) certificates are expensive and time-consuming, as a human must always verify them. Regular Domain Validation (DV) certificates are available for free (Let’s Encrypt), and obtaining and renewing them can be easily automated. EV certificates don’t support OCSP stapling; therefore, always have a DV certificate with stapling enabled.
-
Are you using IPv6?
Studies show that websites are up to 15% faster due to the Neighbor Discovery Protocol (NDP) and route optimization. Update your DNS records to make your website accessible via IPv6. However, don’t forget existing IPv4, as the two versions are not compatible.
-
Is TCP BBR used?
BBR is a relatively new TCP flow control algorithm. It responds to actual congestion rather than packet loss, unlike TCP. As such, it is significantly faster, with higher throughput and lower latency. Enable BBR and set tcp_notsent_lowat to 16 kB for reliable HTTP/2 prioritization on Linux 4.9+ kernels.
-
Always prioritize HTTP/2.
HTTP/2 is well-supported and offers speed enhancements. Depending on how large your mobile user base is, you may need to send different builds (application builds) to users and may benefit from tailoring different builds for different devices. (HTTP/2 is often slower on networks with noticeable packet loss rates.)
-
Deploy HTTP/2 correctly.
You must find the fine balance between merging modules into one and parallel loading of many small modules. Break your entire interface into many small modules; then group, compress, and bundle them. Split at the package level or by tracking which CSS/JS blocks are unused. Separate third-party code from your own and dependencies that change infrequently and frequently. 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. Aim 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. Use tools that compare CDNs (e.g., CDN Comparison) to check your options or quickly look up the level of support for various features. Enable BBR, and set tcp_notsent_lowat to 16 kB for HTTP/2 prioritization.
-
Is HPACK compression used?
If you’re using HTTP/2, check if your servers implement HPACK compression for HTTP response headers to reduce unnecessary overhead. HTTP/2 servers are relatively new and may not fully support the specification, like HPACK. H2spec is an excellent (though technically overly detailed) tool to check this.
-
Prepare for HTTP/3.
On HTTP/2, multiple requests share a connection. On HTTP/3, requests also share a connection but stream independently, so a dropped packet no longer affects all requests, only one 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 send and receive application layer data in the first “round trip” (0-RTT). The bundling of frontend elements still matters, so instead of sending monolithic JS, send multiple JS files in parallel, like on HTTP/2. Expect HTTP/3’s impact on mobile device load times.
-
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 connected to the server before. However, it’s quite demanding on CPU (2–3 times CPU usage for the same bandwidth). Check if your servers or CDN support HTTP over QUIC (also known as HTTP/3) and enable it if possible.
-
Ensure your server 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 via HTTPS, cross-site scripting is impossible, and correct HTTP Strict Transport Security and Content Security Policy headers are set.
Testing and Monitoring
-
Monitor mixed-content warnings.
If you’ve recently migrated from HTTP to HTTPS, remember to monitor both active and passive mixed-content warnings using tools like Report-URI.io. You can also use Mixed Content Scan to scan mixed content on your HTTPS-enabled site.
-
Have you optimized your audit and debugging workflow?
Invest time in studying debugging and auditing techniques in your debugger, WebPageTest, Lighthouse, and extend your text editor. For instance, you can run WebPageTest from a Google Sheet and incorporate 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 and legacy browsers?
Testing in Chrome and Firefox isn’t enough. See 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 your 404 pages’ performance?
Every time a client requests a web element that doesn’t exist, they receive a 404 response – and that response is often huge. Don’t forget to explore and optimize your cache strategy also for your 404 pages. Ensure the browser displays HTML only when expecting an HTML response, and for all other responses return an error response.
-
Have you tested your GDPR consent and cookie banner performance?
Typically, cookie consent prompts shouldn’t impact CLS, but sometimes they might, so consider using free and open source resources like Osano or cookie-consent-box. Consent will likely change the impact of scripts on overall performance, so set up and study several different web performance testing profiles for different consent types.
-
Have you tested the impact on accessibility?
Large pages and DOM manipulation with JavaScript cause screen readers to delay announcements. A fast Time to Interactive means how much time elapses before a screen reader can announce navigation on the page and a screen reader user can actually press the keyboard to interact.
-
Is continuous monitoring set up?
Good speed metrics arise from combining passive and active monitoring tools. A private instance of WebPagetest and using Lighthouse are always beneficial for quick tests, but also setting up continuous monitoring with RUM tools like SpeedTracker, SpeedCurve, and others. Set up your own user-timing marks for measuring and monitoring web-specific metrics.
Quick Wins
The web speed checklist is quite comprehensive, and completing all optimizations might take some time. So, if you only had 1 hour to achieve significant improvements, what would you do? We boil it down to 18 low-hanging fruits. Of course, before you start and once you’re finished, measure results, including Largest Contentful Paint and Time To Interactive metrics on 3G and wired connections.
- Measure real user speed and set appropriate goals. Aim to be at least 20% faster than your fastest competitor. Keep Largest Contentful Paint under 2.5 s, First Input Delay below 100 ms, Time to Interactive under 5 seconds on slow 3G, and for repeat visits under 2 s. 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 image CDN.
- Prepare critical CSS for your main templates and embed it inline in the
<head>of each. For CSS and JS, keep a critical file size budget of max. 170 kB after gzip (0.7 MB unpacked). - Trim, optimize, defer, and lazy-load scripts. Invest in configuring your bundler to remove redundancy and look for less data-heavy alternatives.
- Always host static frontend files on your own domain. Prefer hosting third-party components on your own domain as well. Limit their impact. Use facades and placeholders, load widgets on interaction, and beware of original version flashes.
- Be discerning when choosing a framework. For single-page applications (SPA), identify critical pages and render them statically, or at least prerender them. Use progressive hydration at the component level and import modules upon interaction.
- Client-side rendering alone isn’t a good choice for performance. Prerender if your pages don’t change much, and delay framework booting. Use server-side streaming rendering (SSR).
- Provide old code only to old browsers using the module/nomodule pattern.
- Experiment with reshuffling CSS rules and test CSS served directly in HTML.
- Add resource hints to speed up serving through dns-lookup, preconnect, prefetch, preload, prerender.
- Subset web fonts and load them asynchronously, utilize font-display in CSS for quick first rendering.
- Check if HTTP caches and security headers are correctly set.
- Enable Brotli compression on the server. (If not possible, remember to enable Gzip compression.)
- Enable TCP flow control with BBR if your server runs on a Linux 4.9+ kernel.
- Enable OCSP stapling and IPv6 if possible. 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 ways to avoid rehydration, use progressive hydration and server-side streaming for your single-page application (SPA).
Big 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