◆ booting phatdev.net
<PhatLam/>
000%
← All case studies
Websites and stores · Core Web Vitals · Next.js

phatdev.net: mobile speed from 95 to 99 and content open to both people and AI

My own personal site also runs the docs and plugin updates. I measured it, fixed what sits before the page appears, and made the same content readable by browsers, Google and AI.

Published 8 Nov 2026 · 5 min read

phatdev.net: mobile speed from 95 to 99 and content open to both people and AI
Client
phatdev.net (my own project)
Industry
Portfolio, docs and plugin updates
Year
2026
Role
Planning, architecture and operations (code written with Claude Code)
Duration
Two days, 18 to 19 Oct 2026
Stack
  • Next.js
  • TypeScript
  • MySQL
  • Nginx
  • PM2
  • Lighthouse
  • MCP
99 / 100
PageSpeed mobile / desktop, 18 out of 18 runs
2.8 s → 1.9 s
time for the main part of the home page to show on mobile (LCP)
0
layout shift (CLS) on every page measured
12
read-only MCP tools for AI agents

Background

phatdev.net is where clients and employers look at what I build. It also does two heavier jobs than a normal portfolio: it runs the documentation for my WordPress plugins, and it is the server that ships updates for those plugins. The site has two languages, Vietnamese and English, and no CMS (a ready-made content system such as WordPress). Content lives in a MySQL database, and I wrote the admin area myself.

This is my own project, so no client is named here. I chose it because it has complete before and after numbers, and because every decision in it was mine to own.

The phatdev.net home page on a desktop, dark theme
The phatdev.net home page on a desktop, the live version, dark theme.

The problem

On 18 Oct 2026 the home page scored 95 for mobile speed. Decent, but not what I wanted for the page that represents my work. The slowest part was the wait for the main part of the first screen to show: 2.8 seconds (this measure is called LCP).

Three other things had to be solved at the same time.

First, the two languages had to be right for Google, meaning every page knows where its translation lives. Second, more and more people ask an AI instead of searching, so the content had to be readable by machines without being pulled out of the page design. Third, one person runs the whole site, so it had to tell me who visits and what breaks, without paying for extra tools.

How it was done

1. Measure before changing anything

I ran Lighthouse (Google's speed-scoring tool) from the server itself, simulating a mid-range phone on a slow network, and measured each page at least three times. A single run tells you nothing, because results move depending on whether the machine is busy.

2. Cut what sits before the page appears

The measuring tool works out the display time by adding up everything that must download before that moment. So I went looking for the heaviest thing sitting before it:

  • The avatar weighed about 150 KB but was shown small. Cut to the right size it is 3 to 7 KB.
  • An animation library of about 37 KB existed only for a fade-in effect. I replaced it with CSS and a small script.
  • Four screenshots on the SL Migrations plugin page were PNG files of nearly 1 MB each. As WebP (an image format lighter than PNG) they are about 90 KB.
  • The home page HTML was 19 KB after gzip. When a connection is new, the server can send only about 14.6 KB in its first batch, and anything beyond that waits one more round trip across the network. I switched to brotli (tighter than gzip) in Nginx, the web server software in front of the app. The HTML dropped to 12.2 KB, so it fits in that first batch.

The detailed explanation for developers is in From 95 to 99 on mobile speed: what I did to a Next.js site. Here I keep only the core idea: find the heaviest thing before the LCP point, fix one thing, measure again.

3. One source of data, several ways to read it

Every content page reads from the same place in the database and comes out in four forms: an HTML page for people and Google, a Markdown version (plain text) for machines, an llms.txt index (a list of pages meant for AI), and an MCP endpoint so an AI can ask directly. Because they share one source, the AI version is never older than the human version.

Diagram of a request passing through Nginx, Next.js and MySQL and coming out in four forms: HTML, Markdown, llms.txt and MCP
One request through the site: Nginx, Next.js, MySQL, then four different ways to read it.

Add .md to the end of any content page address and you get the Markdown version of that page. For a text-heavy page such as a blog post it is far lighter than HTML full of layout and effects.

The Markdown version of a blog post, opened in a browser
The same blog post with .md added to the address: machines read plain text instead of stripping out the design.

The MCP endpoint (a standard way for AI to use a website's tools) has 12 tools, all read-only and rate-limited per minute. An AI agent can search projects, read a post or fetch contact details without any key or account. How to connect is in the MCP docs.

4. Running it all on my own

The admin area I wrote records page views on the server, separates real people from bots, groups errors and sends alerts to a dedicated mailbox. Admin accounts support two-step login out of the box. That is how one person can look after the whole system without paying for an outside monitoring service.

Results

These are lab numbers, run with Lighthouse on my own server. They differ from real user data and can be a few points off Google's PageSpeed tool.

PageSpeed score on mobile
Vietnamese home page (/vi)+4%
Before95
After99
English home page (/en)+4%
Before95
After99

Measured with Lighthouse 12 (Google's speed-scoring tool) run from my own server, simulating a phone and a slow network. Before: 18 Oct 2026 (/en scored 95 or 96, the lower is shown). After: 19 Oct 2026, /vi scored 99 in all 3 runs and 18 out of 18 runs on the main pages reached 99 or higher.

Time for the main part of the home page to show (LCP)
Home page /vi, mobile-31%
Before2.8 s
After1.92 s

LCP is the time until the largest part of the first screen has appeared. After the changes, three runs gave 1.81 to 1.92 seconds; the chart uses the highest value.

Data to download
One screenshot on the SL Migrations plugin page-90%
Before900 KB
After90 KB
Whole home page /vi-56%
Before436 KB
After194 KB
Avatar (shown small)-95%
Before150 KB
After7 KB
Home page HTML as sent (compressed)-36%
Before19 KB
After12.2 KB

Screenshots: PNG of 0.8 to 1.1 MB down to WebP of about 90 KB (0.9 MB used). The avatar after optimisation is 3 to 7 KB (7 used). HTML: 19 KB with gzip, 12.2 KB with brotli (two ways to compress a page before sending it, brotli is smaller). The 436 KB was measured on the old build's test port (not behind Nginx) and 194 KB on the live build, so read it as an order of magnitude.

The mobile score went from 95 to 99. On the main pages, 18 out of 18 runs reached 99 or higher, desktop scored 100, and the other three categories, Accessibility, Best Practices and SEO, all scored 100. Layout shift (CLS) stayed at 0.

One thing I could not fix. Total blocking time (TBT) barely moved, because the start-up of React and Next.js (the two libraries that build the page) alone takes about 115 simulated milliseconds, and the site's own code cannot reduce it further.

The phatdev.net home pageThe phatdev-portfolio project pageA blog post about speed
The home page, a project page and a blog post on a phone (390 x 844, live version).

Lessons

Measure three times before you believe a number. One run that says 98 or 99 is luck of the machine.

The part most worth cutting is whatever sits before the LCP point. Shaving bytes from things that load afterwards usually changes nothing.

Making content readable for AI costs little once the site has one clean source of data. The hard part is keeping the machine version and the human version from drifting apart, so I generate both from the same place.

And compression needs an owner. I turned it off inside the app so that Nginx would do it instead. If Nginx is not set up to compress, pages go out with no compression at all, so after a change like this, check the real size of the page.

Need something similar?

If your site is slow on phones, or you want your content readable by AI, send me a few lines about the page. I will measure first, then tell you plainly what is worth fixing and what is not.

Core Web VitalsNext.jsllms.txtMCPPerformance

Learn more