Your shopping cart is empty!
One extra second of load time costs 7% of conversions. That's not a theory — it's data from Google/Deloitte on e-commerce. Page speed affects rankings through Core Web Vitals and directly impacts bounce rates: if a page doesn't load within 3 seconds, 53% of mobile users leave. For most businesses, site speed optimisation is one of the rare improvements that simultaneously boosts traffic, conversions, and rankings.
This guide covers concrete optimisation steps: from images and caching through font loading, the Critical Rendering Path, WordPress plugins, server configuration, and ongoing monitoring. For a performance audit as part of full-cycle SEO promotion, get in touch with the SEO-Factory team.
Contents
Where to Measure Site Speed
Before optimising, you need a baseline. Different tools give different information — and knowing the difference matters.
PageSpeed Insights
Google's free tool at pagespeed.web.dev. Gives a 0–100 score for mobile and desktop, Core Web Vitals data (LCP, INP, CLS), and specific recommendations — what to remove, defer, or compress. Uses both real-world CrUX data and Lighthouse lab data.
Google Search Console → Core Web Vitals
Shows the real-world status of your site's pages based on actual Chrome user data. Categorises pages as "Good", "Needs improvement", or "Poor". The most accurate signal of how Google perceives your site's speed — and the data that directly influences rankings. Full guide to working with the tool — in the article Google Search Console for SEO.
GTmetrix and WebPageTest
Detailed waterfall analysis: see every request, its size, and load time. Useful for identifying specific heavy resources — JS bundles, CSS files, images without lazy loading.
Image Optimisation
Images are the most common cause of slow loading. On most sites they account for 60–80% of total page weight. Three key steps:
Format
Convert images to WebP. Compared to JPEG — smaller file size at the same quality (25–35% savings). Compared to PNG — even greater savings. Supported by all modern browsers.
Dimensions
Don't load a 2,400px-wide image into a slot that displays at 600px. Use the srcset attribute to serve appropriately sized versions for different screen widths.
Lazy loading
Add loading="lazy" to images below the fold. The browser loads them only when the user scrolls to them — the page opens faster for everyone above the fold.
Caching and Compression
Gzip / Brotli compression
Enable compression for text resources at the server level: HTML, CSS, JavaScript. Brotli (supported by Nginx, Apache) outperforms Gzip. Typical result: 60–70% reduction in JS/CSS file sizes.
Browser caching
Set Cache-Control and Expires headers for static assets. For CSS/JS files that rarely change — cache for 1 year. The logic: if the browser has already downloaded a resource, it serves it from cache rather than the server.
File minification
Remove whitespace, comments, and unnecessary characters from CSS, JS, and HTML. Autoptimize for WordPress, Webpack for JS projects. Typical size reduction: 10–30%.
JavaScript and CSS Loading
| Problem | Symptom | Fix |
|---|---|---|
| Render-blocking JS | High TBT, low INP | Add defer or async attributes to scripts |
| Unused CSS | PageSpeed flags "Remove unused CSS" | Critical CSS inline + defer the rest |
| Oversized JS bundle | Long script execution time | Code splitting, tree shaking |
| Third-party scripts (chat, pixels) | LCP suffers from external requests | Load after user interaction or via Partytown |
CDN and Hosting
A CDN (Content Delivery Network) is a global network of servers. When a user opens your site, files are delivered from the nearest node rather than a data centre in another city or country. For sites with audiences across different regions, a CDN cuts TTFB by 40–60%.
If you're not on a CDN yet, the minimum step is switching to hosting with SSD storage and Nginx instead of Apache. The difference in server response time is immediately noticeable.
Priorities: What Speeds Things Up Most
| Action | Complexity | Impact | Priority |
|---|---|---|---|
| Convert images to WebP | Low | High | 1 |
| Lazy loading for images | Low | Medium | 2 |
| Enable Gzip/Brotli | Low | Medium | 3 |
| Defer/async for JS | Medium | High | 4 |
| Browser caching | Low | Medium | 5 |
| CDN | Medium | High (for multi-region audiences) | 6 |
| JS code splitting | High | High | 7 |
The mobile version is almost always slower than desktop. But Google evaluates mobile experience — via Mobile-First indexing. Optimise for mobile first, always.
Font Optimisation
Fonts are a frequently underestimated speed factor. Google Fonts are convenient to set up, but in their default configuration they load from external servers and can block rendering — especially on mobile with a slow connection.
The Google Fonts render-blocking problem
Standard Google Fonts via <link> makes two external requests: first to fonts.googleapis.com for the CSS, then to fonts.gstatic.com for the font files. On a slow connection or cold browser cache, this delays text rendering. The visible effect: FOIT (Flash of Invisible Text) — text is invisible until the font loads, which worsens LCP and perceived speed.
Self-hosting fonts
Hosting fonts on your own server eliminates the external CDN dependency. Combined with <link rel="preload">, the browser fetches the font as early as possible:
<link rel="preload" href="/fonts/inter-v13-regular.woff2" as="font" type="font/woff2" crossorigin>
In the @font-face rule, reference the local woff2 file (smallest, fastest format). Tools: google-webfonts-helper.herokuapp.com downloads the files and generates the CSS.
font-display: swap vs optional vs block
| Value | Behaviour | When to use |
|---|---|---|
| swap | Shows fallback immediately, swaps to custom font after load | Body text — always visible, minor FOUT possible |
| optional | Short block period (100ms), then only from cache | Best for Core Web Vitals — no FOUT, font may not load on first visit |
| block | Blocks text display until font loads (up to 3s) | Avoid — worsens LCP significantly |
| fallback | Very short block (100ms), then fallback permanently | Compromise between swap and optional |
For SEO: use font-display: swap or optional. Swap guarantees text visibility; optional gives the best LCP and zero CLS on repeat visits.
Minimising font weights and variable fonts
Each font weight is a separate file — typically 15–50 KB each. Regular 400 + Bold 700 is the minimum set for most sites. Italic adds two more files. On a site using 6 weights, that's 90–300 KB of fonts alone.
Variable fonts — one file instead of multiple weights. Inter Variable (.woff2) covers all weights from 100 to 900 in a single ~60–80 KB file. Compared to loading 4–6 individual weight files (100–200 KB total), this is a meaningful saving with fewer HTTP requests. Implementation:
@font-face { font-family: 'Inter'; src: url('/fonts/inter-variable.woff2') format('woff2'); font-weight: 100 900; font-display: swap; }
display=swap and select only the weights you actually use. Or download via fontsource.org and host locally — removes the external request and enables preload.Critical Rendering Path
The Critical Rendering Path (CRP) is the sequence of steps the browser performs from receiving HTML to painting pixels on screen. Understanding this sequence lets you target optimisations precisely at the first seconds of load.
The sequence: DOM → CSSOM → Render Tree → Layout → Paint
- DOM (Document Object Model) — the browser parses HTML and builds the element tree. Blocked when it encounters a
<script>without async/defer. - CSSOM (CSS Object Model) — built from CSS files. Page rendering is blocked until all CSS is fully loaded and parsed.
- Render Tree — combination of DOM and CSSOM. Contains only visible elements.
- Layout — the browser calculates sizes and positions for all elements.
- Paint — pixels are drawn on screen. This produces the First Contentful Paint (FCP).
Render-blocking resources
Any <link rel="stylesheet"> in <head> is render-blocking by default. Any <script> without async or defer — likewise. Lighthouse identifies these in "Eliminate render-blocking resources" with an estimated time savings for each.
Critical CSS for Above The Fold
To let the browser render the visible page without waiting for an external CSS file — inline the minimal set of styles for Above The Fold content inside a <style> tag in <head>. Load the rest asynchronously:
<link rel="stylesheet" href="/css/main.css" media="print" onload="this.media='all'">
For WordPress: WP Rocket and LiteSpeed Cache have built-in Critical CSS generation. For other platforms: the critical npm package or Penthouse.
First Contentful Paint (FCP) and why it matters
FCP is the time from navigation start to when the first text or graphical content appears. Good FCP: under 1.8s. While LCP is the direct ranking signal, FCP affects perceived speed and behavioural metrics — particularly on mobile with slow connections.
Above The Fold optimisation
- The LCP element (usually the hero image) — load via
<link rel="preload">in head, not withloading="lazy" - Critical CSS for this zone — inline, not in an external file
- Heading and body fonts — preload
- Third-party scripts (analytics, chat) — defer or load after interaction
WordPress Optimisation
WordPress powers a large share of the web. WordPress itself isn't slow — it's slow plugins, bloated themes, and missing caching that create performance problems.
Top speed plugins: comparison
| Plugin | Caching | Critical CSS | Image opt. | Note |
|---|---|---|---|---|
| WP Rocket | Yes | Auto-generated | LazyLoad | Easiest setup, paid, pays off quickly in performance gains |
| LiteSpeed Cache | Yes | Auto-generated | WebP conversion | Maximum efficiency on LiteSpeed servers, free |
| W3 Total Cache | Yes | No | No | Highly configurable but complex for non-technical users |
| Autoptimize | No | CSS/JS minification | No | Good add-on to any caching plugin |
| Imagify / ShortPixel | No | No | WebP, compression | Specialised image tools, cloud-based compression |
Database optimisation
WordPress accumulates database bloat that slows queries:
- wp-cron — WordPress's pseudo-cron fires on every page request. Under heavy traffic, this creates parasitic load. Disable via wp-config.php (
define('DISABLE_WP_CRON', true);) and configure a real server cron instead. - Transients — temporary data stored in wp_options. Old transients accumulate over time. Clean up with Transients Manager or WP-Optimize.
- Revision cleanup — WordPress stores unlimited post revisions. Limit them:
define('WP_POST_REVISIONS', 3);. Remove old revisions with WP-Optimize.
Disabling unnecessary WordPress features
- Emoji script — WordPress loads wp-emoji-release.min.js by default. If you don't use emoji in content, disable via functions.php or the Disable Emojis plugin.
- Dashicons on the frontend — admin panel icons load for logged-out users if any plugin references them. Check via DevTools → Network and disable if unneeded.
- jQuery Migrate — compatibility library for legacy plugins. If all your plugins are modern, jQuery Migrate is unnecessary. Disable via WP Rocket or functions.php.
Advanced WP Rocket and LiteSpeed Cache settings
Activating the plugin is only half the work. These specific settings deliver measurable gains:
- WP Rocket → Preload Cache: enables cache warming — the plugin crawls your site and caches pages before the first real visitor arrives. Without preloading, the first user after a cache clear gets a slow uncached response.
- WP Rocket → Delay JS Execution: defers JavaScript execution until the first user interaction (click or scroll). Particularly effective at reducing TBT and improving INP on pages with many third-party scripts — analytics, chat widgets, ad pixels.
- LiteSpeed Cache → Object Cache: connect Redis or Memcached via the plugin settings. TTFB for dynamic WordPress pages drops to 30–80ms from a typical 200–500ms. Requires support at the hosting level — check with your provider.
- Imagify / ShortPixel → Bulk Optimisation: run a one-time batch conversion of all existing images to WebP. For large media libraries (1,000+ images), process in batches of 200–300 to avoid overloading the server.
- W3 Total Cache → Fragment Cache: for sites with personalised content (logged-in users, cart counts), fragment caching lets you cache the static parts of a page while keeping dynamic sections live — a significant TTFB improvement without breaking user-specific functionality.
Server Optimisation
TTFB (Time to First Byte)
TTFB is the time from the browser's request to receiving the first byte of the server response. Google's recommended target: under 200ms. TTFB above 600ms is flagged in Lighthouse and directly worsens LCP. Root causes: slow hosting, missing server-side caching, heavy database queries, physical distance to the server.
HTTP/2 and HTTP/3 — multiplexing benefits
HTTP/1.1 allows only 6 parallel connections per domain. HTTP/2 adds multiplexing — multiple requests and responses on a single connection without queuing. HTTP/3 (QUIC) uses UDP instead of TCP — faster connection establishment and better packet loss recovery. Particularly noticeable on mobile networks. Most modern hosting with Nginx 1.25+ and LiteSpeed supports HTTP/3.
Server-side caching: Redis and Memcached
Redis Object Cache for WordPress stores database query results in RAM, avoiding repeated database hits. With proper setup, TTFB drops from 200–400ms to 20–50ms for cached requests.
Shared vs VPS vs Cloud hosting — speed impact
| Hosting type | TTFB (typical) | Scalability | Best for |
|---|---|---|---|
| Shared | 300–800ms | None | Landing pages, blogs under 1k visits/day |
| VPS (SSD) | 80–200ms | Manual | E-commerce, corporate sites |
| Cloud (AWS, GCP) | 50–120ms | Automatic | Sites with unpredictable traffic spikes |
| Managed WordPress | 60–150ms | Limited | Best choice for WP without DevOps resources |
Server region for your target audience
Always choose a server region close to your primary audience. The difference between a Warsaw server and a Singapore server for a European user can be 200–400ms TTFB purely from RTT (Round Trip Time). Verify with WebPageTest.org using a test node in your target geography before committing to a hosting provider.
The most common hosting mistake is choosing by price rather than geography. A $3/month US server for a European audience can deliver TTFB of 400–600ms — no other optimisation compensates for that baseline lag.
Speed Measurement and Monitoring
Tracking performance degradation after updates
Site speed degrades silently: a new plugin, a theme update, adding a third-party script — each change can drop PageSpeed by 5–15 points. Without systematic monitoring, this only becomes visible after rankings have already fallen.
Minimum approach: after every significant update, run PageSpeed Insights for 3–5 key pages (home, category, product page) and log the results in a spreadsheet. This establishes the trend.
Lighthouse CI in CI/CD
Lighthouse CI integrates automatic Lighthouse testing into your deployment pipeline. On every deploy or pull request, Lighthouse runs automatically and results are compared to a baseline. If the Performance Score drops below a threshold — the build can be blocked. Setup for GitHub Actions: the @lhci/cli npm package with a lighthouserc.js config. Free for public repositories.
SpeedCurve and DataDog RUM
SpeedCurve is a real-time performance monitoring platform. It tracks Core Web Vitals for real users, builds trend graphs, and alerts on degradation. It also benchmarks you against competitors — useful for e-commerce where speed directly converts to revenue.
DataDog RUM is a broader monitoring tool that includes Web Performance metrics with segmentation by device, browser, and geography.
WebVitals.js for field data collection
Google's web-vitals library lets you collect real Core Web Vitals from your users and send them to your analytics platform (Google Analytics 4, DataDog, or a custom endpoint):
import {onLCP, onINP, onCLS} from 'web-vitals'; onLCP(metric => sendToAnalytics(metric)); onINP(metric => sendToAnalytics(metric));
This gives you real data from real devices and connections — unlike Lighthouse lab data, which uses a fixed simulated environment.
GSC Core Web Vitals report — how to read it
The Core Web Vitals report in GSC (Experience → Core Web Vitals) shows the status of URL groups: Good / Needs Improvement / Poor. Key points:
- Data is aggregated over a 28-day rolling window — changes appear with a lag
- URL groups — GSC clusters similar pages. An issue in one group may affect hundreds of pages
- Click a URL group → "Open Report" → see specific URLs and metrics
- Fix the issue → request validation in GSC → status updates within 28 days
In Practice
A Kyiv-based pizza chain with in-page online ordering discovered the problem through GSC Core Web Vitals: the menu page LCP on mobile was 7.4 seconds, with the entire URL group flagged "Poor". A GTmetrix waterfall analysis pinpointed the cause — 34 dish photos in JPEG format totalling 9.2 MB, all loaded eagerly without lazy loading, plus the cart script loaded synchronously in <head>, delaying the "Order" button by 3.1 seconds.
Hotjar session data confirmed what the numbers implied: 67% of mobile users left the page before the order button became clickable.
The fix took one week. All 34 food photos were converted to WebP via ShortPixel — the media library shrank from 9.2 MB to 2.6 MB, and below-fold images received loading="lazy". The cart script was moved to defer, which made the "Order" button interactive before full script initialisation.
LCP dropped from 7.4s to 1.8s, and the PageSpeed Insights mobile score rose from 31 to 87. Over the following 4 weeks, menu page conversion — tracked in Google Analytics 4 via the begin_checkout event — increased by 28%.
When the LCP element is a food or product photo, WebP conversion is consistently the highest-ROI single change. But always confirm what the LCP element actually is on mobile before starting — Lighthouse identifies it directly in the Diagnostics section. A 400 KB hero image without preload will dominate your LCP no matter what else you optimise.
Frequently Asked Questions
What page speed is considered good for SEO?
According to Google Core Web Vitals standards: LCP (Largest Contentful Paint) should be under 2.5 seconds, FID/INP under 200ms, CLS under 0.1. A PageSpeed Insights score of 90+ is considered good. These thresholds matter most for mobile, since Google ranks based on the mobile version of your site.
What are the most common causes of slow website loading?
The most frequent causes are: uncompressed images in outdated formats (JPEG/PNG instead of WebP/AVIF), no caching configured, render-blocking JavaScript and CSS in the page head, slow hosting with high TTFB (over 600ms), no CDN, and third-party scripts (live chats, pixels) loaded without defer/async attributes.
How can I check my website speed for free?
Three main free tools: Google PageSpeed Insights (pagespeed.web.dev) — real Chrome user data plus lab analysis; Google Search Console → Core Web Vitals section — aggregated data across your entire site; GTmetrix — detailed waterfall request analysis. Always check both mobile and desktop versions separately.
Does hosting affect page speed and search rankings?
Yes, directly. TTFB (Time To First Byte) — the server response time — depends entirely on your hosting. Good TTFB is under 200ms. If your hosting delivers TTFB above 800ms, no amount of frontend optimization will fully compensate. Moving from shared hosting to a VPS or cloud server (AWS, Google Cloud) typically reduces TTFB by 2–5x.
Slow site hurting your rankings?
SEO-Factory runs performance audits and speed optimisation as part of full-cycle SEO promotion. We analyse PageSpeed scores, identify bottlenecks, and implement fixes with measurable outcomes.
SEO audit & speed optimisation · contextual advertising audit


