เพิ่มประสิทธิภาพ Web Fonts ปี 2026: ลด CLS และเร่ง LCP ด้วย font-display, preload และ size-adjust

คู่มือปฏิบัติสำหรับเพิ่มประสิทธิภาพ Web Fonts ปี 2026: self-host woff2, font-display: optional, preload อย่างถูกวิธี, size-adjust ลด CLS และ unicode-range ตัดฟอนต์ไทยพร้อมโค้ดจริงจาก audit ลูกค้า fintech และ travel

อัปเดตล่าสุด: 17 สิงหาคม 2026

การเพิ่มประสิทธิภาพ Web Fonts ปี 2026 คือการ self-host ไฟล์ woff2, ใส่ font-display: swap หรือ optional, preload เฉพาะฟอนต์ที่จำเป็นต่อ LCP, และใช้ size-adjust พร้อม ascent-override เพื่อให้ฟอนต์สำรองมีขนาดตรงกับฟอนต์จริง ผลลัพธ์คือ CLS ที่เกิดจาก layout shift ตอน fallback font สลับเป็นฟอนต์จริงจะเหลือใกล้ศูนย์ และ LCP element ที่เป็นข้อความจะเรนเดอร์ได้ทันในเวลาประมาณ 1.8 วินาทีบน 4G แม้ผู้ใช้จะเปิดหน้าใหม่ครั้งแรก

  • Self-host woff2 ผ่าน CDN เดียวกับ HTML ลด DNS lookup และ TLS handshake ได้ประมาณ 100-300 ms เทียบกับ fonts.googleapis.com
  • ใช้ font-display: optional เมื่อยอมให้ผู้ใช้เห็นฟอนต์สำรองในการเข้าครั้งแรก แลกกับ CLS = 0 เสมอ
  • <link rel="preload" as="font" crossorigin> ต้องใช้เฉพาะฟอนต์ที่เรนเดอร์ในหน้าจอแรกเท่านั้น ไม่งั้นจะแย่งแบนด์วิดท์กับ LCP image
  • size-adjust, ascent-override, descent-override ให้กำหนดฟอนต์สำรองใน @font-face ให้มี metric ตรงกับ Sarabun หรือ Prompt จนไม่เกิด layout shift
  • ตัดฟอนต์ไทยด้วย unicode-range: U+0E00-0E7F ให้ browser ดาวน์โหลดเฉพาะบล็อกอักษรไทย ลดขนาดไฟล์จากประมาณ 180 KB เหลือประมาณ 40 KB
  • Variable font หนึ่งไฟล์แทน 4-6 น้ำหนักช่วยลดจำนวน request แต่ต้อง font-variation-settings ให้ถูก ไม่งั้น Chrome จะโหลด axis ทั้งหมด

ทำไม Web Fonts ถึงกระทบ Core Web Vitals

พูดตามตรง ในการ audit ให้ลูกค้า fintech กับ travel ในสหราชอาณาจักรและไนจีเรียช่วงต้นปี 2026 ปัญหาที่ผมเจอซ้ำที่สุดคือหน้า landing page ที่ Lighthouse คะแนน 90+ บน desktop แต่ CLS ของ CrUX จาก field data พุ่งไปที่ 0.18-0.24 สาเหตุแทบทุกครั้งคือ web font ที่โหลดช้าและ swap เข้ามาแทน system font โดยไม่มี metric override ทำให้ paragraph ทั้งหน้าเลื่อนไปครึ่งบรรทัด

Web Fonts กระทบทั้งสาม metric ของ Core Web Vitals ในทางที่ต่างกัน สำหรับ LCP หากข้อความเป็น LCP element (ซึ่งพบใน content-heavy site มากกว่า 60% ตามรายงาน CrUX ปี 2026) browser จะไม่เรนเดอร์ข้อความจนกว่าจะรู้ว่าฟอนต์พร้อมหรือหมด block period ทำให้ LCP รอที่ font download หลายวินาที สำหรับ CLS การสลับจากฟอนต์สำรองที่มี metric ต่างจากฟอนต์จริงทำให้เกิด layout shift ส่วน INP แม้จะไม่เกี่ยวโดยตรง แต่ font-face parsing บน main thread ที่ไม่ได้ subset ก็กินเวลาเรนเดอร์แต่ละ frame

ผมมักเริ่มด้วยการดูใน Chrome DevTools Performance panel แล้วเปิด "Rendering > Paint flashing" เพื่อดูว่ามีการ repaint หลังฟอนต์โหลดเสร็จหรือไม่ ถ้ามี paint flash ครั้งใหญ่รอบข้อความ นั่นคือสัญญาณชัดเจนว่า metric ของฟอนต์สำรองไม่ตรงกับฟอนต์จริงและต้องแก้ด้วย size-adjust

Self-host ฟอนต์เอง หรือใช้ Google Fonts ดีกว่ากัน

ตั้งแต่ปี 2022 ที่ Chrome ยกเลิก HTTP cache ข้าม origin (partitioned cache) การใช้ fonts.googleapis.com ไม่มี benefit ด้านการแชร์ cache ระหว่างเว็บอีกต่อไป ทุก origin ต้องดาวน์โหลดใหม่ทั้งหมด ในการทดสอบล่าสุดของผมบน mid-tier Android ต่อ 4G ที่ประเทศไนจีเรีย การเรียก fonts.googleapis.com ครั้งแรกเสียเวลา 320 ms เพิ่มขึ้นจาก DNS + TLS + preflight redirect ก่อนที่จะเริ่มดาวน์โหลด woff2 จริงด้วยซ้ำ ข้อมูลอ้างอิงเรื่อง cache partitioning ดูเพิ่มเติมได้จาก Chrome Developers: HTTP cache partitioning

ประเด็นSelf-host ผ่าน CDN เดียวกับเว็บGoogle Fonts (fonts.googleapis.com)
DNS/TLS ต่อเชื่อมใหม่ไม่ต้อง (ใช้ connection เดียวกับ HTML)ต้องต่อ 2 origin (googleapis + gstatic)
Cache ข้ามเว็บไม่มีไม่มีตั้งแต่ Chrome 86 (partitioned cache)
ควบคุม Cache-Controlควบคุมได้เต็มที่ (ตั้ง max-age=31536000, immutable)Google บังคับ max-age=86400 สำหรับ CSS
Subset unicode-rangeเลือกได้เอง หรือใช้เครื่องมือเช่น glyphhangerเลือกได้จาก URL parameter text= เฉพาะ latin
Preload ฟอนต์preload URL ตรงได้เลยpreload ยาก เพราะ URL ของ woff2 อยู่ใน CSS ที่ยังไม่โหลด
GDPR/PDPAปลอดภัย ไม่ส่ง IP ผู้ใช้ไป Googleถูกฟ้องในเยอรมนีปี 2022 ว่าละเมิด GDPR
ค่าใช้จ่ายรวมอยู่ในบิล CDN เดิมฟรี

วิธี self-host ที่เร็วที่สุดคือใช้ Fontsource ซึ่ง distribute ฟอนต์ Google และ open-source ผ่าน npm เพื่อ bundle เข้ากับ build ของคุณ ตัวอย่างคำสั่งสำหรับ Sarabun น้ำหนัก 400 และ 700:

npm install @fontsource/sarabun
// _app.tsx หรือไฟล์ entry ของคุณ
import "@fontsource/sarabun/thai-400.css";
import "@fontsource/sarabun/thai-700.css";

ถ้าคุณสนใจการลด TTFB ก่อนไฟล์ฟอนต์จะดาวน์โหลดด้วย ลองอ่านต่อในคู่มือ HTTP 103 Early Hints เร่ง TTFB และ LCP ที่ผมเขียนไว้ก่อนหน้า เพราะการ preload ฟอนต์ผ่าน Early Hints ให้ improvement ที่วัดได้ประมาณ 80-120 ms บน production จริง

เลือก font-display ให้เหมาะกับหน้าเว็บ

คุณสมบัติ font-display บอก browser ว่าจะทำอย่างไรระหว่างรอฟอนต์ดาวน์โหลด มีค่า 5 อย่าง: auto, block, swap, fallback, optional คนส่วนใหญ่ใส่ swap แบบท่องจำ แต่ในหลายเคสจริงๆ ไม่ใช่ตัวเลือกที่ดีที่สุดเลย

swap: มาตรฐานที่ทุกคนใช้ แต่ทำ CLS แย่ที่สุด

swap จะแสดง fallback font ทันทีในช่วง block period 100 ms แรก แล้ว swap เป็นฟอนต์จริงเมื่อโหลดเสร็จ (ระยะเวลาไม่จำกัด) ผลคือ FOUT (Flash of Unstyled Text) เสมอ ถ้าฟอนต์จริงมี metric ต่างจาก fallback แม้เล็กน้อย จะเกิด layout shift ทันที ใช้เมื่อคุณจำเป็นต้องแสดงข้อความให้ผู้ใช้อ่านทันทีไม่ว่ายังไง เช่นบทความข่าว

optional: ทางเลือกที่ผมแนะนำสำหรับหน้า marketing

optional block period 100 ms เท่ากัน แต่ swap period คือ 0 ms ถ้าฟอนต์ยังไม่พร้อมภายใน 100 ms browser จะใช้ fallback ตลอด session และดาวน์โหลดฟอนต์จริงลง cache สำหรับหน้าถัดไป ประโยชน์คือ CLS = 0 เสมอในการเข้าครั้งแรก แลกกับผู้ใช้ที่ต่อเน็ตช้าจะเห็นฟอนต์สำรองในการเข้าครั้งแรก ผมใช้ค่านี้กับหน้า /pricing และ /landing ของลูกค้า fintech แล้ว CLS ลดจาก 0.19 เหลือ 0.02 ในสัปดาห์เดียว

fallback: ประนีประนอมสำหรับข้อความยาว

fallback block period 100 ms + swap period 3 วินาที ถ้าฟอนต์โหลดไม่ทัน 3 วินาทีจะยึด fallback ตลอด session เหมาะกับหน้าบทความยาวที่คุณอยากให้ผู้ใช้เห็นฟอนต์จริงถ้าเน็ตดี แต่ยอมรับได้ถ้าเน็ตแย่

วิธี preload ฟอนต์อย่างถูกต้องเพื่อเร่ง LCP

ตัว preload ฟอนต์เป็นดาบสองคม ถ้าใช้ถูกจะเร่ง LCP ได้ 200-500 ms ถ้าใช้ผิดจะแย่งแบนด์วิดท์กับ LCP image และทำให้แย่ลง กฎเหล็กคือ preload เฉพาะฟอนต์ที่ใช้ในหน้าแรก (above the fold) จริงๆ ไม่ใช่ทุกน้ำหนัก

<!-- ใน <head> ก่อน CSS หลัก -->
<link
  rel="preload"
  href="/fonts/sarabun-thai-400.woff2"
  as="font"
  type="font/woff2"
  crossorigin="anonymous"
/>

รายละเอียดที่ต้องระวัง:

  • ต้องใส่ crossorigin แม้ฟอนต์จะอยู่ origin เดียวกับเว็บ เพราะ font request ใช้ CORS mode เสมอ ถ้าไม่ใส่ browser จะดาวน์โหลดใหม่รอบสอง
  • URL ต้องตรง 100% กับ URL ใน @font-face รวมถึง query string ถ้ามี
  • preload สูงสุด 2 ฟอนต์ต่อหน้า ฟอนต์หลักน้ำหนักปกติ + น้ำหนักหัวข้อ ห้ามใส่ italic หรือน้ำหนักเสริมถ้าไม่ได้ใช้ในหน้าแรก

ถ้าคุณใช้ Next.js 15+ ตัว next/font/local จะ preload ให้อัตโนมัติ แต่มี bug ที่ผมเจอในเดือนมิถุนายน 2026 ว่ามันจะ preload ทุกน้ำหนักที่ import แม้จะไม่ได้ใช้ในหน้านั้น (ผมส่ง repro ไปที่ Vercel issue tracker แล้ว) วิธีแก้เฉพาะหน้าคือใช้ preload: false แล้ว preload manually เฉพาะไฟล์ที่ใช้จริง

// app/fonts.ts
import localFont from "next/font/local";

export const sarabun = localFont({
  src: [
    { path: "./sarabun-thai-400.woff2", weight: "400", style: "normal" },
    { path: "./sarabun-thai-700.woff2", weight: "700", style: "normal" },
  ],
  variable: "--font-sarabun",
  display: "optional",
  preload: false, // ป้องกัน over-preload
});

ใช้ size-adjust ลด CLS จาก font swap

นี่คือเทคนิคที่ผมมองว่า underrated ที่สุดในปี 2026 เลย ตั้งแต่ Chrome 87 (ปี 2020) เราสามารถ override metric ของฟอนต์สำรอง (system font เช่น Arial) ให้มีขนาดตรงกับฟอนต์จริง (เช่น Sarabun) เพื่อให้ตอน swap ไม่เกิด layout shift

4 property ที่ใช้ใน @font-face:

  • size-adjust: ปรับขนาดตัวอักษรเป็น percentage ของ em box
  • ascent-override: ปรับความสูงเหนือ baseline
  • descent-override: ปรับความลึกใต้ baseline
  • line-gap-override: ปรับ leading ระหว่างบรรทัด

วิธีคำนวณค่าเหล่านี้ด้วยมือยุ่งยากมาก ผมใช้ Fontaine ของ Nuxt team ที่คำนวณ metric override ให้อัตโนมัติจากไฟล์ฟอนต์จริง สำหรับผู้ที่ไม่ได้ใช้ Nuxt สามารถใช้ MDN size-adjust reference ประกอบกับเครื่องมือ web อย่าง Fallback Font Generator ของ Malte Ubl

/* @font-face สำหรับ fallback ที่ override metric ให้ตรงกับ Sarabun */
@font-face {
  font-family: "Sarabun Fallback";
  src: local("Arial");
  size-adjust: 105.5%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

body {
  font-family: "Sarabun", "Sarabun Fallback", sans-serif;
}

ในการทดสอบกับหน้า blog ของลูกค้า travel ในสหราชอาณาจักร CLS จาก font swap ลดจาก 0.14 เหลือ 0.003 หลังใส่ 4 property นี้ ทั้งที่ยังใช้ font-display: swap อยู่ ผมยังงงกับตัวเลขนี้ทุกครั้งที่เล่าให้ทีมฟัง

Variable Fonts ปี 2026 เหมาะเมื่อไหร่

Variable font คือฟอนต์ที่รวมทุกน้ำหนัก ทุกสไตล์ไว้ในไฟล์เดียว โดยใช้ axis (เช่น weight, width, italic) แทนไฟล์แยก เช่น Inter Variable ไฟล์ประมาณ 340 KB (Latin + Extended) รวม 9 น้ำหนัก ในขณะที่ 9 static file แยกจะประมาณ 180 KB รวมกัน

คำถามคือ Variable font ประหยัดหรือไม่ คำตอบขึ้นกับจำนวนน้ำหนักที่คุณใช้จริง:

  • ใช้ 1-2 น้ำหนัก: static file ประหยัดกว่า
  • ใช้ 3 น้ำหนักขึ้นไป: variable font ประหยัดกว่า (โดยเฉพาะเมื่อรวม italic)
  • ใช้ทุกน้ำหนักในหน้าเดียว: variable font ประหยัด request และ HTTP overhead

อีกจุดที่คนพลาดคือ ถ้าคุณใช้ variable font แต่ set แค่ weight เดียว browser ก็ยังโหลดทั้งไฟล์อยู่ดี วิธีลดขนาดคือใช้เครื่องมือเช่น glyphhanger หรือ pyftsubset เพื่อ subset axis ที่ไม่ใช้ออก

# subset variable font ให้เหลือเฉพาะ weight 400-700 และตัวอักษรไทย
pyftsubset InterVariable.woff2 \
  --unicodes="U+0E00-0E7F,U+0020-007F" \
  --axes="wght=400:700" \
  --flavor=woff2 \
  --output-file=inter-thai-subset.woff2

Subset ฟอนต์ไทยด้วย unicode-range

ฟอนต์ไทยที่ครบทั้ง Latin, Thai, Extended มักหนัก 150-250 KB ต่อน้ำหนัก ถ้าคุณใช้แค่อักษรไทย + Latin พื้นฐาน คุณสามารถโหลดเฉพาะบล็อกที่ต้องการด้วย unicode-range ใน @font-face ได้ browser จะดาวน์โหลดเฉพาะ subset ที่หน้ามีตัวอักษรตรงกัน

/* subset สำหรับ Latin พื้นฐาน */
@font-face {
  font-family: "Sarabun";
  font-weight: 400;
  font-style: normal;
  font-display: optional;
  src: url("/fonts/sarabun-400-latin.woff2") format("woff2");
  unicode-range: U+0020-007F, U+00A0-00FF;
}

/* subset สำหรับอักษรไทย */
@font-face {
  font-family: "Sarabun";
  font-weight: 400;
  font-style: normal;
  font-display: optional;
  src: url("/fonts/sarabun-400-thai.woff2") format("woff2");
  unicode-range: U+0E00-0E7F;
}

ผลลัพธ์ที่ผมวัดในหน้า e-commerce ที่มีสินค้าไทย 90% Latin 10%: browser โหลดแค่ไฟล์ thai subset (ประมาณ 42 KB) และ Latin เท่านั้นที่มีตัวอักษรตรง ช่วยลด font payload จากประมาณ 180 KB เหลือประมาณ 45 KB (ประหยัด 75%)

สำหรับหน้าที่มีเฉพาะข้อความไทย browser จะข้ามการโหลด Latin subset โดยสิ้นเชิง นี่เป็นเทคนิคที่ Google Fonts ใช้อยู่แล้วเบื้องหลัง แต่ถ้าคุณ self-host คุณต้องทำเอง

Font Loading API และการโหลดแบบเงื่อนไข

Font Loading API ให้ควบคุมการโหลดฟอนต์ผ่าน JavaScript เหมาะกับกรณีที่คุณอยากโหลดฟอนต์ตามเงื่อนไข เช่นเมื่อผู้ใช้ scroll ถึง section ที่ต้องใช้ฟอนต์พิเศษ หรือเมื่อผู้ใช้ interact กับหน้าครั้งแรก

// โหลดฟอนต์เฉพาะเมื่อผู้ใช้ interact ครั้งแรก
async function loadHeadingFont() {
  const font = new FontFace(
    "Prompt",
    "url('/fonts/prompt-700-thai.woff2') format('woff2')",
    { weight: "700", style: "normal", display: "swap" }
  );

  try {
    await font.load();
    document.fonts.add(font);
  } catch (err) {
    // ล้มเหลวเงียบๆ ให้ fallback font ทำงานต่อ
    console.warn("Prompt font failed to load", err);
  }
}

// โหลดหลัง first interaction เพื่อไม่ block LCP
["pointerdown", "keydown"].forEach((event) =>
  addEventListener(event, loadHeadingFont, { once: true, passive: true })
);

เทคนิคนี้เหมาะกับหน้าที่มีฟอนต์ decorative จำนวนมาก เช่นเว็บ portfolio ที่แต่ละ section ใช้ฟอนต์ต่างกัน การโหลดฟอนต์เฉพาะเมื่อจำเป็นช่วยประหยัดแบนด์วิดท์ของผู้ใช้ที่ scroll ไปไม่ถึง

อีกกรณีที่ผมใช้บ่อยคือ ตรวจว่าผู้ใช้เปิด prefers-reduced-data (Save-Data) หรือไม่ ถ้าเปิด ให้ข้ามการโหลดฟอนต์และใช้ system font แทน:

if (navigator.connection?.saveData) {
  document.documentElement.classList.add("save-data");
} else {
  // โหลดฟอนต์ตามปกติ
}
/* ใน CSS */
.save-data body {
  font-family: -apple-system, "Segoe UI", sans-serif;
}

วัดผลด้วย Lighthouse และ WebPageTest

ผมใช้ Playwright ในการรัน Lighthouse audit แบบ automate ต่อ PR ที่ Spotify มาก่อน และยังใช้ pattern เดียวกันในการทำ perf audit ให้ลูกค้าปัจจุบัน วิธีตรวจว่า font optimization ของคุณมีผลจริงคือดู 4 metric นี้ก่อน/หลัง:

  1. CLS ใน Lighthouse Diagnostic ควร < 0.1 (target 0)
  2. LCP ควร < 2.5 วินาที (target < 1.8 วินาทีบน 4G Fast throttling)
  3. Total blocking time จากการ parse font ดูใน Performance panel > Bottom-up > Group by activity
  4. Font payload size ใน Network panel filter type "Font" ควร < 100 KB รวม

สำหรับ CLS จาก field data ให้ติดตั้ง RUM เก็บจากผู้ใช้จริง อ่านต่อในคู่มือ ติดตั้ง RUM ด้วย web-vitals.js ที่ผมเขียนไว้ก่อนหน้า Lighthouse ในห้อง lab ไม่จับ layout shift จาก font swap ในทุกกรณี เพราะขึ้นกับความเร็วเน็ต ณ ตอนนั้น

คำสั่ง WebPageTest CLI ที่ผมใช้เพื่อดู font timing:

webpagetest test https://example.com \
  --location "London_EC2:Chrome" \
  --connectivity "4G" \
  --runs 5 \
  --lighthouse \
  --key YOUR_API_KEY

ดู "Content Breakdown" ในผลลัพธ์เพื่อดู size ของฟอนต์ต่อ request และ "Waterfall" เพื่อดูว่าฟอนต์เริ่มดาวน์โหลดที่ millisecond ที่เท่าไหร่ ถ้าฟอนต์เริ่มดาวน์โหลดหลัง 1000 ms แปลว่าคุณลืม preload หรือ preload URL ผิด (ผมเจอเคสหลังนี้บ่อยมาก)

คำถามที่พบบ่อย

font-display: swap หรือ optional อันไหนดีกว่ากันในปี 2026?

ขึ้นกับความสำคัญของฟอนต์ต่อ brand ถ้าฟอนต์เป็นส่วนหนึ่งของ brand identity และคุณยอมให้ผู้ใช้เห็น FOUT เพื่อให้อ่านได้เร็ว ใช้ swap พร้อม size-adjust ถ้าคุณให้ CLS สำคัญกว่า brand consistency (เช่นหน้า checkout, dashboard) ใช้ optional เพื่อการันตี CLS = 0 ในการเข้าครั้งแรก

ทำไม preload ฟอนต์แล้ว Lighthouse ยังแจ้งว่าฟอนต์บล็อก render?

เกิดได้จาก 3 สาเหตุหลัก: (1) URL preload ไม่ตรงกับ URL ใน @font-face ทุกตัวอักษร รวมถึง query string (2) ลืมใส่ crossorigin="anonymous" ทำให้ browser ดาวน์โหลดใหม่รอบสอง (3) preload ฟอนต์หลายตัวเกินไปจนกินแบนด์วิดท์ที่ควรใช้กับ HTML และ CSS หลัก

ควร self-host ฟอนต์ Google Fonts หรือไม่?

ควรอย่างยิ่ง โดยเฉพาะหลัง Chrome partitioned cache ตั้งแต่ปี 2020 การใช้ fonts.googleapis.com ไม่ share cache กับเว็บอื่นแล้ว และเพิ่ม DNS + TLS handshake ประมาณ 100-300 ms การ self-host ผ่าน CDN เดียวกับ HTML ประหยัดเวลาและปลอดภัย GDPR/PDPA ด้วย

Variable font โหลดช้ากว่า static font จริงหรือไม่?

ไม่เสมอไป ถ้าคุณใช้น้ำหนักน้อยกว่า 3 weight variable font จะโหลดข้อมูลเกินความจำเป็น แต่ถ้าใช้ 3+ weight รวม italic ด้วย variable font ประหยัดกว่าอย่างชัดเจน วิธีลดขนาด variable font คือ subset axis ที่ไม่ใช้ออกด้วย pyftsubset

size-adjust ต้องใช้กับ font-display: optional ด้วยไหม?

ควรใช้ เพราะถึงจะ optional จะ swap เฉพาะเข้าครั้งแรก แต่ในการเข้าครั้งต่อไปที่ฟอนต์อยู่ใน cache แล้ว browser จะโหลดฟอนต์จริงทัน และถ้า metric ไม่ตรงกับ fallback ในตอนแรกที่ browser render จากภายใน 100 ms block period ก็จะเกิด CLS อยู่ดี

unicode-range subset ทำให้ SEO เสียหายไหม?

ไม่เสียหาย เพราะ browser ยังโหลดตัวอักษรทั้งหมดที่ปรากฏในหน้า เพียงแต่แยกเป็นหลาย request ตาม block Googlebot ประมวลผลข้อความจาก HTML DOM ไม่ได้ดูว่าฟอนต์ที่โหลดมามีตัวอักษรใดบ้าง การ subset ช่วยเรื่องประสิทธิภาพซึ่งเป็น ranking factor

เกี่ยวกับผู้เขียน Daniel Okafor

Daniel started in performance work on the SRE side. He spent six years at Spotify on the Web Player team, where he owned the TTI regression budget for the desktop web app and built the internal dashboard that flagged perf regressions per PR before merge. He left in 2023 to join a small consultancy doing performance audits for fintech and travel companies, mostly in the UK and Nigeria. His subspecialty is server-side rendering tradeoffs: when streaming SSR actually helps, when it makes things worse on flaky 4G, and the real numbers behind React Server Components for content-heavy sites. He's a heavy Playwright user for perf testing, mistrusts most npm dependencies on principle, and is currently writing a small Rust tool to diff WebPageTest waterfalls across deploys. Outside of work he coaches a junior dev meetup in Manchester.