WordPress speed optimization tutorial content usually focuses on “getting a higher score.” The real benefit is bigger: faster sites improve user experience, reduce bounce, and increase the number of pages viewed per session. Additionally, speed improvements often reduce server load and improve stability during traffic spikes.

Search engines also use performance signals as part of overall page experience. Therefore, improving Core Web Vitals and fixing common Lighthouse warnings helps both rankings and usability.

Table of Contents

What this WordPress speed optimization tutorial will help you achieve

If your WordPress site is stuck at 60–80 Lighthouse, it usually isn’t because WordPress is “slow.” It’s because themes and plugins load too much CSS/JS on every page, fonts block rendering, images are oversized, and third-party scripts (ads/analytics/recaptcha) add heavy main-thread work.

This tutorial fixes that in a safe order that beginners can follow:

  • A correct testing workflow (so results stop “jumping around”)
  • A safe functions.php starter pack that works on most WordPress sites
  • A practical method to reduce Total Blocking Time (TBT) without breaking menus/forms
  • Image + font fixes that directly improve Core Web Vitals
  • A debug function so you can see what assets load and decide what to delay/lazy-load

I’ll also show “In my case…” examples using my real functions.php approach (Gillion child theme + performance + accessibility + debug tooling), so you can map the same logic to your site.


The Two Real Websites I Optimized

I worked on two production WordPress websites:

yousafjanutmanzai.com

  • Initial Lighthouse score: ~68
  • Issues: render-blocking scripts, accessibility warnings, unused JS/CSS, font loading problems

tigerzplace.com

  • More complex site (news + Elementor + ads + heavy scripts)
  • Took more time, but results were even better

The same principles applied to both sites, which means you can apply this to almost any WordPress website.

WordPress speed optimization showing 95 Lighthouse performance score after optimization
Final Lighthouse performance score after complete WordPress speed optimization and Core Web Vitals fixes.

1) Test speed correctly (Lighthouse speed test + PSI + GTmetrix)

Most people optimize blindly because the testing is inconsistent. Fix the testing first.

Tools to use

  • Lighthouse speed test: Chrome → DevTools → Lighthouse
  • Google PageSpeed Insights WordPress: online check + hints
  • GTmetrix WordPress optimization: waterfall, request priorities, and history

Testing rules that stop false results

  1. Open a new Incognito window (no extensions).
  2. Purge all caches (cache plugin + server cache + CDN cache).
  3. Run the test 3 times and compare the middle result.
  4. Test 3 page types:
    • Homepage
    • One category/archive page
    • One single post
  5. Save “Before” screenshots. You’ll need proof and context later.

Want this done for you?

This tutorial explains how WordPress speed optimization works. If you prefer a safe, manual optimization done for you (Lighthouse, Core Web Vitals, GTmetrix), you can hire me directly here: Get In Touch HERE


2) What to fix first (Core Web Vitals priorities)

When you want to improve Lighthouse score WordPress, you’ll see many audits. Don’t chase everything at once. Fix in this order:

Priority order (fastest path to 80–90+)

  1. Caching + server response (TTFB, repeated loads)
  2. Render-blocking resources (CSS + JS delivery)
  3. Total Blocking Time (heavy scripts, third-party, page builders)
  4. Images (LCP image size, lazy-load below the fold)
  5. Fonts (font-display swap, remove duplicate font loaders)
  6. CLS (layout shifts from images/logo/ads)
  7. Accessibility warnings (often easy, boosts Lighthouse “quality”)
Lighthouse performance metrics explained for WordPress speed optimization
Explanation of Core Web Vitals used in WordPress speed optimization.

3) Step 1: Create a Child Theme (mandatory)

If you edit the parent theme, updates overwrite everything. A child theme keeps your speed fixes permanent.

Manual child theme structure

Create:

  • /wp-content/themes/yourtheme-child/
  • style.css
  • functions.php

style.css

/*
Theme Name: YourTheme Child
Template: yourtheme
Version: 1.0
*/

functions.php

<?php
add_action('wp_enqueue_scripts', function () {
wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css');
wp_enqueue_style(
'child-style',
get_stylesheet_directory_uri() . '/style.css',
['parent-style'],
'1.0'
);
}, 20);

In my case: I used a Gillion child theme and specifically avoided duplicating the parent stylesheet because the theme already enqueues its main handle.


4) Step 2: Safe functions.php starter pack (copy/paste for beginners)

This section is the “almost works for any WordPress user” pack. It avoids risky changes and focuses on safe cleanup + performance wins.

Add these blocks in your child theme functions.php.
If you already have similar code, do not duplicate.

4.1 Safe Head Cleanup (small win, zero breakage)

// Remove some default meta links (safe)
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'wp_generator');

4.2 Disable Emojis (safe + common)

add_action('init', function () {
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('admin_print_scripts', 'print_emoji_detection_script');
remove_action('wp_print_styles', 'print_emoji_styles');
remove_action('admin_print_styles', 'print_emoji_styles');
remove_filter('the_content_feed', 'wp_staticize_emoji');
remove_filter('comment_text_rss', 'wp_staticize_emoji');
remove_filter('wp_mail', 'wp_staticize_emoji_for_email');
});

4.3 Disable Embeds (usually safe)

If you don’t use embed features heavily, this reduces overhead.

add_action('init', function () {
wp_deregister_script('wp-embed');
});

4.4 Disable comment-reply where not needed (safe)

add_action('wp_enqueue_scripts', function () {
if (!is_singular() || !comments_open() || !get_option('thread_comments')) {
wp_dequeue_script('comment-reply');
}
}, 999);

4.5 Add display=swap to Google Fonts (safe)

This helps the “text invisible during load” audit.

add_filter('style_loader_src', function ($src) {
if (strpos($src, 'fonts.googleapis.com/css') === false) return $src;
if (stripos($src, 'display=swap') === false) {
$src .= (strpos($src, '?') !== false ? '&' : '?') . 'display=swap';
}
return $src;
}, 10, 1);

4.6 Make Google Fonts non-blocking (optional, usually safe)

This can improve render time but may cause a short font swap. If you dislike that, skip this block.

add_filter('style_loader_tag', function ($html, $handle, $href, $media) {
if (strpos($href, 'fonts.googleapis.com') === false) return $html;
// Make it non-render-blocking
$optimized = str_replace('rel="stylesheet"', 'rel="stylesheet" media="print" onload="this.media=\'all\'"', $html);
$optimized = str_replace("rel='stylesheet'", "rel='stylesheet' media='print' onload=\"this.media='all'\"", $optimized);
return $optimized . '<noscript>' . $html . '</noscript>';
}, 10, 4);

4.7 Resource hints (use ONE place only)

If your cache plugin already does preconnect/dns-prefetch, don’t duplicate it here.

add_filter('wp_resource_hints', function ($hints, $relation_type) {
if ($relation_type === 'preconnect') {
$hints[] = 'https://fonts.gstatic.com';
$hints[] = 'https://fonts.googleapis.com';
}
if ($relation_type === 'dns-prefetch') {
$hints[] = '//fonts.googleapis.com';
$hints[] = '//fonts.gstatic.com';
}
return array_values(array_unique($hints));
}, 10, 2);

5) Step 3: Images (optimize WordPress images + LCP safety)

Image mistakes are the biggest “page weight” problem on WordPress.

The rules that work on almost every site

  • Upload correct sizes (don’t upload 6000px images for 800px containers)
  • Convert to WebP (or AVIF if your stack supports it well)
  • Lazy-load below-the-fold images
  • Protect the LCP image (don’t lazy-load it)

Practical functions.php fix (LCP-safe)

This makes the featured image load faster on single posts.

add_filter('wp_get_attachment_image_attributes', function ($attr, $attachment, $size) {
$attr['loading']  = $attr['loading'] ?? 'lazy';
$attr['decoding'] = $attr['decoding'] ?? 'async';
if (is_singular() && !empty($attachment->ID)) {
$thumb_id = (int) get_post_thumbnail_id();
if ($thumb_id && (int)$attachment->ID === $thumb_id) {
$attr['loading']       = 'eager';
$attr['fetchpriority'] = 'high';
}
}
return $attr;
}, 10, 3);

Insert Image: Image optimization proof (before vs after size)

  • Alt text: “optimize WordPress images by converting to WebP and reducing file size”
  • Caption: “Smaller images reduce load time and improve Speed Index.”

6) Step 4: Fonts (font-display swap WordPress + icon fonts)

Fonts are tricky because they often come from:

  • theme folders
  • Elementor/page builders
  • icon libraries (FontAwesome/Themify/Simple Line Icons)

Beginner approach (recommended)

  • Use a local fonts plugin (like OMGF-style approach)
  • Ensure your theme isn’t also loading Google Fonts (avoid duplicates)
  • Keep display=swap

Advanced approach (what I did in my case)

I implemented font-display: swap for icon fonts by detecting the actual font file paths and injecting an inline @font-face. This is more advanced but solves Lighthouse font warnings very effectively.

In my case: FontAwesome came from Elementor plugin paths, while Themify/Simple Line Icons were theme-related.


7) Step 5: Reduce Total Blocking Time (defer JavaScript WordPress safely)

If Lighthouse shows high TBT, JavaScript is blocking the main thread. The goal is to delay/defer non-critical scripts without breaking your site.

What usually causes high TBT

  • page builders loading bundles globally
  • sliders and animation scripts
  • ads + analytics scripts
  • reCAPTCHA loading everywhere
  • multiple social widgets

Safe “defer” approach for beginners

Do not defer jQuery blindly. Start with safe scripts only:

  • wp-embed
  • comment-reply (already conditionally removed above)

If you still want a minimal defer:

add_filter('script_loader_tag', function ($tag, $handle) {
$safe_defer = ['wp-embed', 'comment-reply'];
if (in_array($handle, $safe_defer, true)) {
if (stripos($tag, ' defer') !== false || stripos($tag, ' async') !== false) return $tag;
return str_replace(' src', ' defer src', $tag);
}
return $tag;
}, 10, 2);

After you’re stable: delay/defers using a plugin

Most serious TBT reductions come from a caching/performance plugin’s Delay JS feature. The correct workflow is:

  1. Delay “selected,” not “all”
  2. Exclude critical scripts (menus, forms, builders)
  3. Test the homepage + one post + one category

8) Step 6: Plugin audit (the speed you don’t need is the speed you should remove)

A slow site is often plugin bloat.

Remove plugins when

  • They duplicate features you already have
  • They load site-wide CSS/JS for a feature used on one page
  • They inject multiple third-party scripts
  • They are unused “just in case” tools

Keep plugins that matter

  • one caching/performance plugin (only one)
  • one SEO plugin
  • one security plugin (light settings)
  • essential builder (if needed)
GTmetrix waterfall chart showing render blocking resources in WordPress
GTmetrix waterfall view used to identify slow and render-blocking WordPress assets.

9) Step 7: Debug mode (this is what makes optimization repeatable)

This section turns your tutorial from “tips” into a real system.

Goal of debugging

You need to see:

  • which scripts/styles load on a page
  • their handles
  • whether they load in footer
  • what dependencies they have

Then you can decide:

  • what to delay
  • what to unload on pages where it’s unused
  • what to exclude from aggressive CSS/JS optimization

9.1 Debug function (Console asset table)

Here is a clean, beginner-friendly debug tool you can paste into functions.php.

How it works

  • Only admins can use it
  • Open any page with: ?tz_assets=1
  • It prints two tables in the browser console: Styles and Scripts
add_action('wp_footer', function () {
if (!current_user_can('manage_options')) return;
if (!isset($_GET['tz_assets'])) return;
global $wp_styles, $wp_scripts;
$styles = [];
if ($wp_styles instanceof WP_Styles) {
foreach ((array) $wp_styles->queue as $h) {
$o = $wp_styles->registered[$h] ?? null;
if (!$o) continue;
$styles[] = [
'handle' => $h,
'src'    => $o->src,
'ver'    => $o->ver,
'deps'   => implode(', ', (array) $o->deps),
'media'  => is_string($o->args) ? $o->args : '',
];
}
}
$scripts = [];
if ($wp_scripts instanceof WP_Scripts) {
foreach ((array) $wp_scripts->queue as $h) {
$o = $wp_scripts->registered[$h] ?? null;
if (!$o) continue;
$in_footer = (!empty($o->extra['group']) && (int)$o->extra['group'] === 1) ? 'yes' : 'no';
$scripts[] = [
'handle'    => $h,
'src'       => $o->src,
'ver'       => $o->ver,
'deps'      => implode(', ', (array) $o->deps),
'in_footer' => $in_footer,
];
}
}
echo '<script>
console.group("WP Enqueued Assets");
console.table(' . wp_json_encode($styles) . ');
console.table(' . wp_json_encode($scripts) . ');
console.groupEnd();
</script>';
}, 999);

How to use the output

  1. Open your homepage as admin:
    • https://yoursite.com/?tz_assets=1
  2. Press F12 → Console
  3. You will see two tables. Look for:
    • huge bundles (Elementor/WPBakery/slider libraries)
    • third-party scripts (ads, analytics, recaptcha)
    • scripts that load on pages where they are not used

Simple decision rules

  • If a script is only needed on one page → conditionally load it (advanced) or exclude it from delay (safe)
  • If a script is third-party and not critical → delay it
  • If a script controls menu/header → do not delay it
  • If a script powers forms/checkout → do not delay it

10) My real-world example (how I applied this on my site)

Here’s how the same principles looked in an actual setup (Gillion child theme + page-builder assets + fonts + ads + recaptcha).

What I implemented in my case (high-level)

  • A TZ debug collector to log data to console (admin-only)
  • Safe head cleanup
  • Resource hints (preconnect/dns-prefetch)
  • Google Fonts display=swap + non-blocking loading
  • Defer list for non-critical handles
  • Icon font swap injection for FontAwesome/Themify/Simple Line Icons
  • LCP-safe featured image behavior (eager + fetchpriority high)
  • Preload of the LCP image where appropriate
  • Conditional disabling of WPBakery assets when not used
  • Accessibility fixes for “discernible name” overlay links and heading structure
  • Lazy-loading reCAPTCHA by removing its auto-load and loading it on first interaction

That’s the “advanced layer.” Beginners don’t need all of it on day one, but it’s a perfect example of what your site can evolve into once you learn the debug workflow and confirm what is safe to delay/unload.

WordPress speed optimization progress showing Lighthouse and GTmetrix improvement history
Performance improvement timeline during the WordPress speed optimization process.

11) Final checklist (copy/paste into your workflow)

One-time setup

  • Child theme created and active
  • One caching/performance plugin selected
  • Baseline functions.php pack added
  • Images converted and resized (WebP recommended)
  • Fonts fixed (display=swap + avoid duplicates)
  • Debug tool enabled (?tz_assets=1)

Ongoing (monthly)

  • Re-test Lighthouse + GTmetrix
  • Audit newly added plugins
  • Optimize new featured images
  • Re-check TBT after plugin/theme updates

FAQ: WordPress speed optimization problems and fixes

A stable 90–95 is usually better than a fragile 100 that breaks menus/forms or hurts ad revenue.

Mobile CPU throttling is harsher, and third-party scripts hurt more. Therefore, delay scripts carefully and reduce total JS.

No. GTmetrix uses Lighthouse data but displays it differently and adds extra diagnostics like waterfall. Therefore, use both: Lighthouse for score direction, GTmetrix for request-level debugging.

Lighthouse simulates network/CPU conditions. Scores can vary. Therefore, run three tests and use the middle result.

Menus, sliders, and forms. Therefore, delay “selected scripts” and add exclusions for essential files.


External links


Final verdict

WordPress speed optimization becomes easy when the process is consistent: test correctly, enable solid caching, fix render-blocking resources, reduce total blocking time, and optimize images and fonts. After that, maintain stability by avoiding duplicate performance plugins and auditing scripts that load site-wide. Follow the checklist in this WordPress speed optimization tutorial and the site will not only score better in Lighthouse, it will also feel faster for real visitors.