Website Performance Optimization: From 53 to 97 on PageSpeed Insights
Introduction
Recently, in order to improve the user experience of my site, I decided to conduct a targeted performance optimization using PageSpeed Insights analysis.
Initially, I optimized the site based on the Cactus theme, raising the score from 47 to 94. During the writing of this post, I switched the theme to Icarus, which caused some previous optimizations to become invalid. The mobile score plummeted to 53 overnight. I had to start over, but by applying the same methodology, I eventually pushed the PageSpeed Insights score to 97.
Initial Status Analysis

The PageSpeed Insights report highlighted the following major issues:
| Metric | Initial Value | Issue |
|---|---|---|
| First Contentful Paint (FCP) | 7.1s | Too Slow |
| Largest Contentful Paint (LCP) | 15.0s | Severely Slow |
| Cumulative Layout Shift (CLS) | 0.339 | Significant Shift |
| Speed Index | 5.2s | Poor Experience |
Note: Metrics varied during different test runs, but the core issues remained the same.
Key performance bottlenecks included:
- Oversized Font Files: MesloLG font in TTF format was as large as 636KB.
- Font Awesome CSS: Loaded the full icon library (~19KB) even though only a few icons were used.
- Missing Preconnect: No preconnect hints for critical resources like CDN.
- Unoptimized Images: The Logo image was 447x432 but displayed at 88x88.
- Layout Shift: Images lacked width/height attributes, causing CLS issues.
Round 1: Resource Slimming
1. Font Format Conversion (TTF → WOFF2)
WOFF2 is currently the most efficient Web font format, offering compression rates far superior to TTF.
1 | # Convert font using ttf2woff2 |
Result: 636KB → 160KB, a 75% reduction.
Then update the font reference in CSS to prioritize WOFF2:
1 | @font-face |
2. Add Preconnect Hints
Add resource preconnects in the <head> to allow the browser to establish TCP connections in advance:
1 | <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin> |
3. Logo Optimization
The original Logo was 447x432 pixels, but only needed 88x88 for display.
sips (Scriptable Image Processing System) is a built-in command-line image processing tool on macOS. You can perform image resizing and format conversion without installing any third-party software.
1 | # Resize image using sips |
Note: If you are using Linux or need more complex image processing, you can use ImageMagick’s
convertcommand as an alternative.
Result: 32KB → 7.6KB, a 76% reduction.
4. Fixing CLS Layout Shift
Add explicit dimension attributes to the Logo image:
1 | <img id="logo" src="/images/Logo_Sketch_88.png" width="50" height="50" /> |

Score after Round 1: 53 → 61
Round 2: Removing Font Awesome
Analysis showed that the Font Awesome CSS was about 19KB, plus even larger font files, but I only used the following icons:
- ☰ Menu Icon (fa-bars)
- ‹ › Pagination Arrows (fa-angle-left/right)
- Social Media Icons (GitHub, YouTube, Bilibili, etc.)
Solution: Replace with SVG
1. Menu Icon
1 | <!-- Before --> |
2. Pagination Arrows
Use Unicode characters directly:
1 | <!-- Before --> |
3. Social Media Icons
Create an SVG icon mapping object:
1 | var svgIcons = { |
Then remove the Font Awesome CSS reference:
1 | <!-- Remove this line --> |

Score after Round 2: 61 → 94 (Mobile), 100 (Desktop)
Why is removing Font Awesome so effective?
The core logic lies in the collapse of the “Critical Request Chain.”
Before: The browser had to go through a 4-level waterfall: HTML → style.css → all.min.css → fa-solid-900.woff2. Each level requires a full RTT (Round Trip Time). On a slow mobile network, this is how 2.4s of latency accumulates.
After: SVGs are inlined directly in the HTML byte stream. When the browser parses the icon’s position, the drawing instructions are already ready. The request chain length drops from 4 to 0.
When I opened the browser’s Network panel and saw all.min.css slowly performing its handshake, subsequently triggering the download of two 100+KB font files, I couldn’t sit still. The entire page was left blank for 2 seconds just for those few pagination arrows and menu icons.
Why I finally decided to “ax” Font Awesome:
In Android development, we would never integrate a multi-MB SDK just to display three icons. But in Web development, includingall.min.cssseems to have become a “default operation.”
Round 3: SEO & Broken Link Fixes
While pursuing performance, I discovered an overlooked SEO issue: dead links in the source code.
1. Eliminate /page/0/ 404 Links
Many Hexo themes (including Icarus) have a pagination logic that works like this:
- On the first page, the “Previous” button is set to
is-invisible(hidden visually) via CSS, but the HTML source still generates<a href="/page/0/">Previous</a>. - Search engine crawlers (like Googlebot) ignore CSS styles and follow the
href. Since/page/0/doesn’t exist, this leads to numerous 404 errors, dragging down the site’s authority.
Solution: Localized Component Rewrite
I extracted the paginator.jsx component locally and modified the rendering logic:
1 | // Before: Generates <a> regardless |
2. Remove Blocking Progress Bar (Pace.js)
The report showed pace.min.js as a long chain request. Although it shows a page loading progress bar:
- It is located in the
<head>, making it a blocking resource. - It does nothing to help LCP (Largest Contentful Paint) and instead consumes network bandwidth.
Operation: Disable it directly in _config.icarus.yml.
Round 4: Modern Image Formats & Adaptive Sizing
After solving code and font issues, the only remaining “heavyweight” in the PageSpeed Insights suggestions was Image Resources.
The report pointed out two main problems:
- Oversized Images: For example, a 1536px wide cover image displaying at ~360px on mobile, wasting huge amounts of bandwidth and slowing down LCP.
- Legacy Formats: Still using JPG/PNG instead of WebP, which offers far better compression.
Solution: Automated WebP Conversion & Resizing
To solve this fundamentally, I wrote an automated script based on the Node.js sharp library to clean up main images across the site:
1 | // Script logic summary: Automated batch processing |
Specific Optimization Cases
Ditherpunk Demo Image (
returnofobradinn)- Before: 533KB (JPG)
- After: ~26KB (WebP, 672px)
- Reduction: ~95%. This was decisive for the LCP improvement.
Post Cover Image (
esp32_dither_cover)- Before: 1536x1587 resolution (533KB)
- After: 672x694 resolution (~28KB)
- Adaptive Sizing: Strictly matched container width (672px), eliminating “Oversized Image” warnings.
M5Stack Cardputer Cover (
cardputer-streaming-cover)- Before: 1200x900 resolution
- After: 600x450 resolution (~20KB)
- For pages with smaller display areas, the width was further limited to 600px to match the actual rendering size (599px).
Sidebar Thumbnails
- Before: ~20KB (original image shrunk)
- After: ~3KB (WebP, 120px)
- Generated specialized 120px micro-versions to avoid loading large images for small icons.
Site Logo
- Before: PNG format (32KB)
- After: WebP format (8.1KB) with transparent background support.
Further Squeeze: Adjusting Compression Factor
While converting to WebP helped, the default quality of 80 was still overkill for some large images. PageSpeed Insights suggested “Efficiently encoding images,” so I re-processed them with higher compression:
1 | // Using sharp's extreme compression parameters |
These small optimizations added up, eventually maxing out the LCP score. Total image volume across the site was reduced by over 1MB.
Round 5: Eliminating Critical Request Chain Blocking
Even after optimizing images and fonts, PageSpeed Insights still flagged a 441ms “Critical Request Chain” block, caused by CSS requests to fonts.googleapis.com and subsequent font downloads.
Although we introduced local WOFF2 fonts in Round 1, the Hexo theme (layout/common/head.jsx) still loaded Google Fonts (Ubuntu, Source Code Pro) by default.
Actions
- Remove Google Fonts: Commented out the Google Fonts loading code in the theme. Since we already configured a local font stack (Meslo LG / System Fonts), this was safe and immediately saved 441ms of blocking time.
- Async Load Non-Critical CSS: For code highlighting styles (
atom-one-light.css), which don’t affect initial content reading (LCP elements are usually titles or cover images), I switched to asynchronous loading:
1 | <!-- Before: Render-blocking --> |
After these “surgeries,” the critical request chain depth dropped from 5 to 2, and FCP (First Contentful Paint) was significantly reduced.
Conclusion
While writing this article, I initially used the
hexo-cactus-theme. During the process, I switched tohexo-icarus-theme. When I first switched to Icarus, the score dropped below 60. However, by applying the optimization principles described here—even with a home page that displays images instead of pure text like Cactus—the score eventually reached 97.
| Optimization | Original (Icarus Default) | Optimized | Reduction |
|---|---|---|---|
| MesloLG Font | 636KB (TTF) | 160KB (WOFF2) | 75% |
| Logo Image | 32KB | 8.1KB | 74% |
| Image Resources | JPG/PNG (Oversized) | WebP (Adaptive) | > 90% |
| Font Awesome | ~19KB + Font | 0KB (Inline SVG) | 100% |
| Google Fonts | 441ms Block | 0ms (Removed) | 100% |
| Preconnect hints | None | 4 preconnects | - |
| CLS Score | 0.339 | ~0 | - |
| PageSpeed Score | Icarus Initial | Optimized |
|---|---|---|
| Mobile | 53 | 97 |
| Desktop | 67 | 100 |

Lessons Learned
- Fonts are a major factor: Web fonts are often overlooked performance killers; WOFF2 is the preferred format.
- Use icon libraries sparingly: If you only use a few icons, inlined SVG is much more efficient than loading a whole library.
- Images on demand: Ensure image dimensions match their display dimensions.
- Preconnect works: For necessary third-party resources,
preconnectcan save DNS + TCP time. - CLS is easy to fix: Giving images explicit dimension attributes can avoid most layout shifts.
- Cache Lifecycle (TTL): Beyond resource size, use a CDN like Cloudflare to force long-term caching for static assets (
Cache-Control: max-age=31536000). This ensures that when a user visits a second article, fonts and CSS load instantly from the Disk Cache.
Tools Recommended
- PageSpeed Insights - Google’s official performance testing tool.
- WebPageTest - Detailed waterfall analysis.
- Squoosh - Google’s online image compression tool.
- ttf2woff2 - Tool for converting TTF to WOFF2.
Hope this article helps you! If you are also optimizing your website’s performance, feel free to reach out and exchange ideas.
Website Performance Optimization: From 53 to 97 on PageSpeed Insights