เว็บโหลดช้าเพราะรูป กลยุทธ์ optimize image แบบครบวงจรสำหรับ developer ไทย 2026
รูปภาพคือปัญหาเบอร์หนึ่งของ Web Performance
ในมุม technical รูปภาพกินสัดส่วนของขนาดเพจยุคใหม่แบบถล่มทลาย ข้อมูล HTTP Archive ธันวาคม 2025 เพจ desktop ขนาด median ~2,312 KB โดยรูปคิดเป็น 1,026 KB (44.4%) บน mobile สัดส่วนสูงกว่า ~47%
แปลว่า ถ้าต้อง optimize แค่ 1 อย่างของเว็บ เลือกรูปภาพ ถูกที่สุด
ที่สำคัญกว่านั้น รูปส่งผลตรงกับ Core Web Vitals ของ Google โดยเฉพาะ LCP (Largest Contentful Paint) จาก CrUX report กว่า 70% ของ LCP element คือรูปหรือ block ที่มี background image LCP เกิน 2.5 วินาที = "Needs Improvement" เกิน 4 วินาที = "Poor" กระทบ SEO ranking โดยตรง
ต่อไปรื้อทีละส่วนในมุม architecture
เคสจริงที่เคยเจอ: LCP พุ่งจาก 1.8 วินาทีเป็น 6.2 วินาที
บอกตามตรง ปีที่แล้วผมรับงานลูกค้าเจ้าหนึ่ง LCP พุ่งจากโซนเขียว (1.8 วินาที) ไปโซนแดง (6.2 วินาที) เพียงแค่เปลี่ยนรูป hero หน้าแรกรูปเดียว ตอนแรกนึกว่า JS bundle ระเบิด debug อยู่ 3 วันถึงเจอ — ดีไซเนอร์คนใหม่อัปโหลด PNG ขนาด 4.5MB ความละเอียด 1920×1080 ขึ้นหน้าแรก ไม่บีบ ไม่มี loading attribute ไม่มี fetchpriority และไม่ผ่าน CDN ทดสอบแล้วพบว่ารูปเดียวนี้เลยที่ฉุด LCP ทั้งหมด
หลังจัดการ 3 เรื่องต่อไปนี้ LCP กลับมาที่ 1.9 วินาที
- แปลง PNG เป็น WebP บีบลงเหลือ 180KB (ลดไป 96%)
- เติม
fetchpriority="high"และ<link rel="preload"> - ย้ายรูปขึ้น Cloudflare R2 + Transform Rules ทำ AVIF fallback อัตโนมัติ
กับดักนี้มีคนตกเยอะ พูดตรงๆ รูป hero หน้าแรกคือจุดชี้เป็นชี้ตายของ LCP ขาดอะไรไปสักขั้น Core Web Vitals ทั้งหน้ารายงานเป็นสีแดงทันที แต่ละหัวข้อต่อจากนี้กลั่นมาจากเคสจริงแบบนี้ทั้งนั้น ไม่ได้ก็อปมาจากเอกสาร
Lazy Loading: ชะลอโหลดรูปนอก viewport
Native HTML
วิธีง่ายสุดคือใช้ attribute loading="lazy" ของ browser
<img src="photo.webp" loading="lazy" alt="ตัวอย่างรูป" width="800" height="600">
พื้นฐานคือ browser จะส่ง HTTP request เฉพาะตอนรูปใกล้เข้า viewport Chrome implement ที่ระยะ 1250px (desktop) หรือ 2500px (slow network) ตอนผู้ใช้ scroll ถึงรูปก็โหลดเสร็จแล้ว
ระวัง: รูปใน fold แรก (above the fold) ไม่ควร ตั้ง lazy loading จะทำให้ LCP ช้า รูป above-the-fold ใช้ loading="eager" (default) + fetchpriority="high"
JavaScript: Intersection Observer
ถ้าต้องการควบคุมละเอียดกว่า ใช้ Intersection Observer API
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
}, { rootMargin: '200px' });
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));
ข้อดี — ปรับ trigger distance (rootMargin), ใส่ fade-in animation, หรือ show LQIP (Low Quality Image Placeholder) ก่อนโหลดรูปจริง
เปรียบเทียบ 2 วิธี
| หัวข้อ | Native loading="lazy" |
Intersection Observer |
|---|---|---|
| Complexity | ต่ำมาก (attribute เดียว) | กลาง (เขียน JS) |
| Browser support | 95%+ (2026) | 97%+ |
| Custom trigger distance | ไม่ (browser เลือก) | ได้ (rootMargin) |
| LQIP placeholder | ไม่รองรับ | รองรับ |
| SEO | ดี (native) | ต้อง noscript fallback |
| Risk ใช้ผิดใน fold แรก | มี (ต้อง exclude เอง) | exclude ใน code ได้ |
แนะนำ: เคสส่วนใหญ่ใช้ native พอ ใช้ Intersection Observer เฉพาะเคสที่ต้อง LQIP หรือควบคุมละเอียด
Responsive Images: ให้แต่ละอุปกรณ์โหลดขนาดที่พอ
รูป hero กว้าง 3000px บนมือถือต้องการแค่ 750px ถ้าไม่ทำ responsive images user มือถือโหลดรูปใหญ่เกินไป 4 เท่าเปล่า ๆ
srcset + sizes
<img
srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w, hero-1600.webp 1600w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
src="hero-800.webp"
alt="Homepage banner"
>
Browser เลือกรูปเหมาะสุดจาก srcset ตาม viewport width + pixel density sizes บอก browser ว่ารูปกินกว้างเท่าไหร่ใน breakpoint ต่าง ๆ เลือกได้แม่นกว่า
<picture> element: คุม format + art direction
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Homepage banner">
</picture>
<picture> ส่ง format ต่างกันให้ browser ต่างกัน ใช้ format ที่บีบดีสุดที่ browser รองรับ auto fallback
กลยุทธ์เลือก format
Format ส่งผลตรงกับขนาดไฟล์และคุณภาพ ตัวเลขทดสอบจริง (เงื่อนไข: รูป 1920x1080 เดียวกัน visual quality เทียบเคียง)
| Format | ขนาดทั่วไป | Browser support | Transparency | Animation | เหมาะกับ |
|---|---|---|---|---|---|
| JPEG | baseline 100% | 100% | ไม่ | ไม่ | รูปถ่าย (compat สูงสุด) |
| WebP | ~70% ของ JPEG | 97%+ | ใช่ | ใช่ | รูปทั่วไป (แนะนำ default) |
| AVIF | ~50% ของ JPEG | 92%+ | ใช่ | ใช่ | รูปถ่าย (compression ดีสุด) |
| PNG | JPEG 300-500% | 100% | ใช่ | ไม่ | รูปที่ต้อง transparency |
| SVG | เล็กมาก (vector) | 100% | ใช่ | ใช่ | icon, logo, chart |
กลยุทธ์แนะนำ: AVIF → WebP → JPEG progressive fallback ใช้ <picture> ให้ browser เลือก auto
CDN และ Caching Strategy
ทำไม CDN สำคัญ
รูปจากเซิร์ฟเวอร์ไทยส่งไปยูเซอร์อเมริกา ระยะทางกาย ~12,000 km ความเร็วแสงใน fiber latency ~40ms (ทางเดียว) บวก TCP handshake, TLS, HTTP request — TTFB (Time To First Byte) ของรูป 1 ใบอาจเกิน 200ms
CDN cache รูปไว้ที่ edge node ทั่วโลก ให้ user ได้ resource จาก node ใกล้สุด CDN ดี ๆ ลด TTFB จาก 200ms+ เหลือ 20-50ms
Cache Header
Cache-Control: public, max-age=31536000, immutable
รูปมักไม่เปลี่ยน ตั้ง cache 1 ปี + immutable บอก browser ไม่ต้อง revalidate ถ้ารูปต้องอัปเดต เปลี่ยนชื่อไฟล์ (hash) เพื่อ invalidate
Image CDN
Cloudflare Images, Imgix, Cloudinary ฯลฯ นอกจาก CDN ปกติ ยังให้
- Format conversion on-the-fly (auto เลือก AVIF/WebP/JPEG ตาม Accept header)
- Resize on-the-fly (ใช้ URL parameter กำหนด width/height)
- Quality auto optimization (ปรับ compression ตาม network speed)
Image Compression Pipeline
ในมุม architecture pipeline ที่สมบูรณ์ประกอบด้วย
- Upload-time compression: ใช้ Sharp (Node.js) หรือ Pillow (Python) บีบฝั่ง server
- Multi-size generation: สร้าง 3-5 ขนาด (400w, 800w, 1200w, 1600w, original)
- Multi-format conversion: ทำ AVIF, WebP, JPEG ทั้ง 3 format สำหรับแต่ละ size
- Quality tuning: JPEG quality 75-85, WebP quality 75-80, AVIF quality 60-70
- Metadata stripping: ลบ EXIF ลดขนาด + ปกป้อง privacy
ตัวอย่าง รูป 1920x1080 หลังผ่าน pipeline user มือถือโหลดจริงอาจเป็น AVIF 400w ขนาด ~30KB ลดจาก 2MB เป็น ~30KB — ลดการส่ง 98.5%
ผลของ Core Web Vitals
LCP (Largest Contentful Paint)
LCP วัดเวลา render ของ largest visible element สำหรับเพจที่ LCP element คือรูป
- ดี: ≤ 2.5 วินาที
- ต้องปรับ: 2.5-4 วินาที
- แย่: > 4 วินาที
Optimize รูป above-the-fold
fetchpriority="high"เพิ่มความสำคัญของการดาวน์โหลด<link rel="preload">preload LCP image- ห้าม lazy load รูปใน fold แรก
- รับประกัน cache hit rate ของ CDN
CLS (Cumulative Layout Shift)
ถ้าไม่กันพื้นที่รูปก่อนโหลด layout กระโดด แก้ง่าย: ใส่ width + height ใน <img> เสมอ browser คำนวณ aspect ratio ก่อนรูปโหลดได้
<img src="photo.webp" width="800" height="600" alt="Example">
Checklist Image Optimization
ก่อน launch ตรวจทีละข้อ
- รูป above-the-fold ตั้ง
fetchpriority="high", รูปอื่นloading="lazy" -
<img>ทุก tag มีwidth+height - ใช้
<picture>ส่ง AVIF/WebP/JPEG fallback - ใช้
srcset+sizesส่งหลายขนาด - รูปผ่าน compression JPEG 75-85, WebP 75-80
- CDN เปิด cache header ถูก (
max-age=31536000, immutable) - EXIF metadata ถูกลบ
- LCP image มี
<link rel="preload"> - PageSpeed Insights LCP ≤ 2.5 วินาที
เคสในบริบทไทย
เว็บข่าวไทยยักษ์ใหญ่ (Thairath, Dailynews, Prachachat)
Traffic สูงมาก การ optimize รูปทุก % ประหยัด bandwidth หลักล้านบาทต่อเดือน กลยุทธ์หลักคือ (1) Cloudflare หรือ Akamai CDN (2) image resize โดย backend ล่วงหน้า (3) AVIF + WebP + JPEG fallback (4) lazy load ทุกรูปใน article page ยกเว้นหน้าปก
E-commerce ไทย (Shopee Mall, Lazada Store, ร้านบน WooCommerce)
Product listing page มีรูปหลายสิบรูป lazy loading + responsive images สำคัญมาก แนะนำ Cloudinary หรือ Imgix ที่แปลง format on-the-fly + ปรับ size URL-based
Blog และ Blockdit ของคนไทย
ผู้เขียน content ส่วนใหญ่ไม่มี engineer ช่วยทำ compression pipeline แนะนำ (1) Squoosh บีบก่อนอัป (2) ใช้ image hosting ที่มี CDN ในเอเชีย (เช่น lpic.cc, ImgBB) (3) ถ้าใช้ WordPress ติด plugin เช่น Smush หรือ ShortPixel
LINE OA Landing Page
เพจ Landing จาก Rich Menu ของ LINE OA โหลดในเบราว์เซอร์ภายในแอป ปรับ optimize แบบเว็บทั่วไป + preload รูปหลักเพราะ user อาจปิดก่อนรูปโหลด
ISP ต่าง ๆ ในไทยกับความเร็วโหลดรูป
- AIS, True: peer กับ Cloudflare/Google ดี รูปจาก CDN โหลดเร็ว
- 3BB: stability ดี ส่วนใหญ่รูปโหลดปกติ
- NT: peer กับ Japan ดีพิเศษ ถ้า CDN node Tokyo เร็วเป็นพิเศษ
ดังนั้นเลือก CDN ที่มี node Tokyo หรือ Singapore จะครอบ ISP ไทยทั้งหมด
บทบาทของ Image Hosting
ถ้าไม่อยาก build image pipeline + CDN เอง ใช้ image hosting พร้อมใช้เป็นทางเลือกที่ practical
ตัวอย่าง lpic.cc ให้อัปรูปไม่ต้องสมัคร + CDN global + password protection สำหรับ blog writer และเว็บเล็กในไทย ประหยัดต้นทุน build infrastructure เอง แต่แจ้งว่า lpic.cc ไม่รองรับ API integration และ resize on-the-fly ถ้า project ต้อง batch upload เชิงโปรแกรม หรือ dynamic transform พิจารณา Cloudinary หรือ Imgix
คำถามที่พบบ่อย FAQ
Q1: Lazy loading กระทบ SEO ไหม
ไม่กระทบ Googlebot run JavaScript และ simulate scroll ได้ index รูปที่ใช้ loading="lazy" ถูก แต่ถ้าใช้ custom JavaScript lazy load ใส่ <noscript> fallback เผื่อให้ search engine ตามรูปได้
Q2: WebP กับ AVIF ใช้อันไหน
Compression — AVIF ชนะ (เล็กกว่า JPEG ~50%) แต่ encoding AVIF ช้ากว่า ไม่เหมาะ realtime conversion กลยุทธ์ที่ดีคือใช้ <picture> ส่งทั้งคู่ browser เลือก auto ถ้าเลือกได้แค่ตัวเดียว WebP compatibility สูงกว่า (97% vs 92%) safer
Q3: Image CDN คุ้มค่าใช้จ่ายไหม
ขึ้นกับ traffic ถ้าเพจต่ำกว่า 100K PV/เดือน Cloudflare free plan (มี basic CDN) พอแล้ว เกินนั้น professional image CDN เช่น Imgix หรือ Cloudinary ที่ auto format conversion + resize ลด bandwidth cost ได้ ระยะยาวคุ้ม
Q4: บีบอัดรูปทำให้คุณภาพหายไหม
Lossy compression มี quality loss แน่ แต่ที่ quality setting เหมาะ (JPEG 75-85, WebP 75-80) ตาคนแทบไม่รู้สึก แนะนำวัดด้วย SSIM — SSIM > 0.95 user ส่วนใหญ่แยกก่อน/หลังไม่ออก
Q5: เว็บใช้ CDN แล้ว ยังต้อง optimize รูปอีกไหม
ต้อง CDN แก้แค่ "ระยะส่ง" ไม่แก้ "ปริมาณส่ง" รูป 5MB ที่ไม่ได้ optimize ต่อให้ CDN ใกล้สุดก็ต้องส่ง 5MB format conversion + size adaptation + compression ลด bandwidth ได้ 80-95% ผลกว่าการใช้ CDN อย่างเดียว
Q6: Next.js / Nuxt ในไทย มี image optimization built-in ไหม
Next.js มี <Image> component handle responsive + lazy + format conversion ให้ run บน Vercel ได้ดี deploy บน custom server ต้องตั้ง image optimization service เอง Nuxt 3 มี <NuxtImg> ทำได้คล้าย
Q7: ทำ Shopify theme สำหรับร้านค้าไทย optimize รูปยังไง
Shopify เอง handle responsive และ format conversion (WebP) ใน theme ใหม่ ๆ ใช้ {{ image | image_url: width: 800 }} Liquid helper ได้ srcset + format อัตโนมัติ กับธีมที่ไม่ support ให้ใช้ Shopify Image Optimization app