Feb 2026

Next.js Server Cache

ปัญหา Cache ระหว่าง Deployment

คู่มือฉบับสมบูรณ์เกี่ยวกับโครงสร้าง Caching 4 ชั้นของ Next.js

Next.js
Server Cache
Deployment
Optimization

1. Introduction: เข้าใจ Server Cache ของ Next.js

Server Cache คืออะไร?

Server Cache หรือ การเก็บแคชข้อมูลบน Server เป็นเทคนิคที่ช่วยให้แอปพลิเคชันสามารถเก็บสำเนาของข้อมูลหรือผลลัพธ์จากการประมวลผลไว้ในหน่วยความจำหรือที่จัดเก็บ เพื่อสามารถนำมาใช้ซ้ำได้โดยไม่ต้องประมวลผลใหม่ทุกครั้ง

ในบริบทของ Next.js, Server Cache มีบทบาทสำคัญมากในการเพิ่มความเร็วของแอปพลิเคชันและลดภาระของ Server โดยเฉพาะในการ Deploy แอปพลิเคชันเว็บใหม่ๆ ซึ่งการเข้าใจและจัดการ Cache อย่างถูกต้องจะช่วยป้องกันปัญหาที่เกิดขึ้นเมื่อมีการ Deploy แอปใหม่

ประโยชน์ของ Server Cache

  • Performance: ลดเวลาในการโหลดหน้าเว็บ
  • Scalability: ลดภาระของ Server และ Database
  • User Experience: ให้ประสบการณ์ผู้ใช้ที่รวดเร็วขึ้น
  • Cost: ลดค่าใช้จ่ายในการประมวลผล

ปัญหา Cache ที่พบบ่อย

  • Old Assets ค้างใน Browser
  • Stale Data บนหน้าเว็บ
  • CDN Cache ไม่ถูกปรับปรุง
  • Cache Busting ไม่ทำงาน

Key Insight

"ไม่ใช่ทุกปัญหาที่ดูเหมือนจะเป็น Bug จริงๆ ปัญหาบางอย่างเกิดจากการตั้งค่า Infrastructure และ Configuration รอบๆ โค้ด" - การเข้าใจระบบ Cache เป็นสิ่งสำคัญที่สุด

2. Next.js Four-Tier Caching System

Next.js มีระบบ Caching ที่ซับซ้อนและมีประสิทธิภาพสูงโดยใช้ 4 ชั้นหลัก ที่ทำงานร่วมกันเพื่อเพิ่มความเร็วและปรับปรุงประสิทธิภาพของแอปพลิเคชัน

Cache Type Mechanism Location Purpose Duration
Request Memoization Return values of functions Server Re-use data in a single render Per-request
Data Cache Fetched data Server Share data across users Persistent
Full Route Cache HTML & RSC Payload Server Speed up page rendering Persistent
Router Cache RSC Payload Client Speed up navigation Session/Time

1. Request Memoization

การเก็บค่า return ของฟังก์ชันไว้ในหน่วยความจำระหว่างการ render หนึ่งครั้ง ฟีเจอร์นี้ทำงานโดยอัตโนมัติใน Next.js

app/page.tsx
async function fetchUser(id: string) {
  const res = await fetch(`https://api.example.com/users/${id}`)
  return res.json()
}

// First call - cache MISS
const user1 = await fetchUser('123')

// Second call - cache HIT
const user2 = await fetchUser('123')
ใช้ได้เฉพาะ GET requests และภายใน React Component Tree เท่านั้น

2. Data Cache

การเก็บข้อมูลที่ fetch ไว้อย่างถาวรและสามารถใช้ร่วมกันระหว่างผู้ใช้หลายคนและ deployments

app/page.tsx
export default async function Page() {
  const data = await fetch('https://...', { 
    cache: 'force-cache',
    next: { revalidate: 3600 }
  })
}
Data Cache จะถูก Invalidate เมื่อ Full Route Cache เปลี่ยนแปลง

3. Full Route Cache

การเก็บ HTML และ React Server Component (RSC) Payload สำหรับเส้นทางทั้งหมด ช่วยให้หน้าเว็บโหลดได้เร็วขึ้น

app/page.tsx
const Product = async () =gt; {
  const products = await getProducts()
  return (
    <div>
      <h1>Products</h1>
      <ul>{products.map(p =gt; (<li>{p.title}</li>))}</ul>
    </div>
  )
}
Static pages ถูก build ครั้งเดียวแล้ว serve ให้ผู้ใช้หลายครั้ง

4. Router Cache

การเก็บ RSC Payload ของแต่ละ route segment ไว้ใน memory ของ browser เพื่อให้การนำทางเร็วขึ้น

app/page.tsx
import { useRouter } from 'next/navigation'

export default function Page() {
  const router = useRouter()
  
  // Handled automatically by Next.js
  return (<Link href="/about">About</Link>)
}
Default: 5 นาทีสำหรับ static pages, 30 วินาทีสำหรับ dynamic

Cache Interactions

การเข้าใจว่า Cache แต่ละชั้นทำงานร่วมกันอย่างไรเป็นสิ่งสำคัญในการแก้ไขปัญหา Cache

Interaction Effect
Invalidate Data Cache Invalidates Full Route Cache
Invalidate Full Route Cache Does NOT affect Data Cache
revalidatePath/Tag in Server Action Invalidates both Data & Router Cache
Revalidate Data in Route Handler Does NOT affect Router Cache immediately

3. ปัญหา Cache ที่พบบ่อยระหว่าง Deployment

กรณีศึกษา: ปัญหา Cache จริง

หนึ่งในปัญหาที่พบบ่อยที่สุดคือ หลังจาก deploy แอปใหม่ เว็บไซต์ไม่โหลดข้อมูลล่าสุด หรือไฟล์ JavaScript ไฟล์เก่าค้างอยู่ใน browser ทำให้เกิด error หรือแอปใช้งานไม่ได้

Symptom:

  • Application โหลดไม่ได้ใน browser ปกติ
  • โหลดได้ปกติใน Incognito mode
  • ไฟล์ JS served with 304 Not Modified
  • Network requests ดึงไฟล์เก่าจาก cache

1. Old Assets in Browser

Browser ยังคงใช้ JavaScript, CSS หรือ image files เก่าที่ cache ไว้ แม้จะ deploy แอปใหม่แล้ว

Cause: Cache-Control headers ไม่ถูกต้อง หรือไม่มี cache busting

2. Stale Data

Data ที่ได้จาก API หรือDatabase ยังเป็นข้อมูลเก่าแม้จะมีการอัปเดตข้อมูลแล้ว

Cause: Data Cache ไม่ถูก revalidate หรือ data fetched with cache: 'no-store' ถูกเก็บไว้

3. CDN Cache Issues

CDN ยังคง serve ไฟล์เก่าหรือ content เก่าที่ cache ไว้จาก deployment ก่อนหน้า

Cause: CDN cache ไม่ถูก purge หลัง deployment หรือ TTL เทียบยาวเกินไป

Checklist ตรวจสอบ Cache Issues

Development:

Disable cache ใน browser devtools
Check Network tab ดู status code
Clear DevTools > Application > Storage

Production:

Test ใน incognito/private mode
Check Cache-Control headers
Purge CDN cache หลัง deploy

4. Solutions & Best Practices

กลยุทธ์ Cache Busting

Cache Busting คือเทคนิคที่ใช้บังคับให้ browser หรือ CDN โหลดไฟล์เวอร์ชันล่าสุดแทนที่จะใช้ไฟล์ที่ cache ไว้ มีหลายวิธีในการทำ cache busting

Method 1: Build ID / Versioning

ใช้ file name hash หรือ version number เพื่อบ่งชี้เวอร์ชัน

// next.config.js
module.exports = {
  generateBuildId: async () =gt; 'v' + Date.now().toString(16),
}

Method 2: Cache-Control Headers

กำหนด header เพื่อควบคุมระยะเวลา cache

// Static assets
Cache-Control: public, max-age=31536000, immutable

// Dynamic content
Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate

Method 3: Query Parameters

เพิ่ม version query parameter ลงใน URL

<script src="/app.js?v=1.2.3"></script>
<link rel="stylesheet" href="/styles.css?cache-bust=12345"/>

Method 4: ReactQuery SWR

ใช้ stale-while-revalidate pattern

const { data } = useSWR('/api/data', {
  revalidateOnFocus: false,
  refreshInterval: 60000, // 60s
})

next.config.js Configuration

next.config.js
// Configure cache settings for all routes
module.exports = {
  headers: async () =gt; [
    {
      source: '/(.*)',
      headers: [
        {
          key: 'X-Content-Type-Options',
          value: 'nosniff',
        },
        {
          key: 'X-Frame-Options',
          value: 'DENY',
        },
        {
          key: 'X-XSS-Protection',
          value: '1; mode=block',
        },
      ],
    },
  ],
  images: {
    domains: ['images.example.com'],
    formats: ['image/avif', 'image/webp'],
  },
}

vercel.json Configuration

vercel.json
{
  "headers": [
    {
      "source": "/api/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "no-cache, no-store, must-revalidate" }
      ]
    },
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
      ]
    }
  ],
  "rewrites": [
    { "source": "/(.*)", "destination": "/api/proxy" }
  ]
}

Best Practices Summary

1

Explicit Headers

Always configure Cache-Control headers explicitly

2

CDN Purge

Automate CDN cache purging during deployment

3

Version Hashing

Use file name hash for immutable assets

4

Revalidate Data

Set appropriate revalidation periods

5

Test Both Modes

Test in both normal and incognito modes

6

Monitor Logs

Regularly monitor cache headers in production

7

ISR Strategy

Use Incremental Static Regeneration for dynamic content

8

Protect Sensitive

Never cache sensitive user data

9

Use Tags

Use cache tags for fine-grained revalidation

5. Code Examples & Use Cases

Example 1: API Data Caching

ตัวอย่างการใช้ Data Cache กับ API data โดยใช้ cache tag

app/api/products/route.ts
import { NextResponse } from 'next/server'
import { cacheTag } from 'next/cache'

export async function GET() {
  cacheTag('products')
  
  const res = await fetch(
    'https://api.example.com/products',
    {
      next: { revalidate: 3600 }
    }
  )
  
  const data = await res.json()
  return NextResponse.json(data)
}

ใช้ cacheTag เพื่อกำหนด tag ให้ data และใช้ revalidate: 3600 เพื่อ revalidate ทุก 1 ชั่วโมง

Example 2: On-Demand Revalidation

ตัวอย่างการใช้ on-demand revalidation กับ Server Actions

app/lib/actions.ts
'use server'

import { revalidateTag } from 'next/cache'
import { db } from '@/lib/db'

export async function createProduct(formData: FormData) {
  // Create product in database
  await db.product.create({
    data: {
      name: formData.get('name'),
      price: Number(formData.get('price')),
    }
  })

  // Invalidate cache for products
  revalidateTag('products')
  revalidatePath('/products')

  redirect('/products')
}

After creating a new product, we invalidate both the cache tag and the route path to ensure fresh data

Note:

Use revalidateTag('products', 'max') for stale-while-revalidate behavior instead of immediate invalidation

Example 3: Incremental Static Regeneration (ISR)

ตัวอย่างการใช้ ISR เพื่อ update หน้าเว็บโดยไม่ต้อง rebuild ทั้งหมด

app/products/[slug]/page.tsx
import { notFound } from 'next/navigation'
import { cache } from 'react'

const getProduct = cache(async (slug: string) =gt; {
  const res = await fetch(
    `https://api.example.com/products/${slug}`,
    {
      next: { tags: ['product', `product-${slug}`] }
    }
  )
  
  if (!res.ok) return null
  return res.json()
})

export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params
  const product = await getProduct(slug)
  
  if (!product) return notFound()
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>Price: ${product.price}</p>
    </div>
  )
}

// Generate static paths at build time
export async function generateStaticParams() {
  const products = await fetch('https://api.example.com/products')
    .then(r =gt; r.json())
  
  return products.slice(0, 100).map(p =gt; ({ slug: p.slug }))
}

Mechanism:

  • 100 products ถูก generate ที่ build time
  • Product ที่ยังไม่ได้ generate จะถูกสร้างทันทีที่มีการ request
  • Data จะถูก revalidate ทุกครั้งที่มี request (stale-while-revalidate)

Example 4: Automate CDN Purge on Deploy

ตัวอย่าง script สำหรับ purge CDN cache หลัง deployment

package.json
{
  "scripts": {
    "build": "next build",
    "deploy": "npm run build && vercel --prod && npm run purge-cache",
    "purge-cache": "node scripts/purge-cache.js"
  }
}
// scripts/purge-cache.js
const { execSync } = require('child_process')

const project_id = 'your-project-id'
const token = 'YOUR_VERCEL_API_TOKEN'

// Invalidate all caches
execSync(`curl -X POST https://api.vercel.com/v10/deployments/${project_id}/cache \
  -H "Authorization: Bearer ${token}" \
  -H "Content-Type: application/json" \
  -d '{"cache": "invalidate_all"}'`)

console.log('✓ CDN cache purged successfully!')

แนะนำให้ใช้ GitHub Actions หรือ CI/CD pipeline เพื่อ automate การ purge cache หลัง deployment

6. Troubleshooting: Debug Cache Issues

Debugging Checklist

Browser DevTools

1. Network Tab: Check for 304 status codes on assets
2. Headers: Look for Cache-Control and ETag headers
3. Disable Cache: Check "Disable cache" in Network tab
4. Application: Clear Storage & Cache Storage

Server/Environment

1. Check .next directory size (clear if needed)
2. Verify buildId is different after rebuild
3. Clear Vercel/CDN cache via dashboard
4. Test locally with next start

Problem: Old Assets Load

Symptoms:

  • Site doesn't load in normal tab
  • Works in Incognito mode
  • 304 Not Modified on JS files

Solution:

// 1. SetCache-Control headers
res.setHeader('Cache-Control', 
  'no-store, no-cache, must-revalidate, proxy-revalidate')

// 2. Clear browser cache
// 3. Add cache buster query param
<script src="/app.js?v=1.2.3"></script>

Problem: Stale Data Shows

Symptoms:

  • Content not updating after changes
  • Data not reflecting recent changes
  • ISR pages not revalidating

Solution:

// 1. Use on-demand revalidation
revalidateTag('content')
revalidatePath('/articles')

// 2. Set shorter revalidation time
const res = await fetch(url, {
  next: { revalidate: 60 }  // 1 minute
})

Tools for Debugging

Next.js Dev Tools

Enable strict mode and check cache status

next dev --experimental-strict

Vercel Dashboard

Monitor cache hits/misses and deployment status

vercel deploy --prod --prod

Cache Purge API

Programmatically purge caches

POST /v10/deployments/:id/cache

7. คำศัพท์ภาษาไทย (Thai Terminology)

English Term Thai Translation Description
Server Cache แคชของ Server การเก็บข้อมูลหรือผลลัพธ์ที่ได้จากการประมวลผลไว้ในหน่วยความจำของ Server
Cache Busting การขจัดแคช เทคนิคที่ใช้บังคับให้ browser โหลดไฟล์เวอร์ชันล่าสุดแทนไฟล์ที่ cache ไว้
Revalidation การตรวจสอบและอัปเดตแคช การตรวจสอบว่าข้อมูลในแคชยังใช้งานได้หรือไม่ และอัปเดตข้อมูลใหม่เมื่อจำเป็น
On-Demand Revalidation การตรวจสอบและอัปเดตแคชแบบเรียกใช้ การเรียกใช้การตรวจสอบและอัปเดตแคชเมื่อมีเหตุการณ์เฉพาะเกิดขึ้น
Time-Based Revalidation การตรวจสอบและอัปเดตแคชตามเวลา การอัปเดตแคชโดยอัตโนมัติทุกช่วงเวลาที่กำหนด
CDN Cache แคชของ CDN การเก็บสำเนาของไฟล์และข้อมูลไว้ที่ server ใกล้กับผู้ใช้มากที่สุด
Cache Purge การลบแคช การลบข้อมูลที่ cache ไว้ออกทั้งหมดเพื่อบังคับให้โหลดใหม่
Stale Data ข้อมูลที่ไม่อัปเดต ข้อมูลที่อยู่ในแคชแต่ไม่ได้อัปเดตตามข้อมูลล่าสุด
Cache Tag แทแท็กของแคช string ที่ใช้ระบุและจัดกลุ่มข้อมูลในแคชเพื่อให้สามารถลบได้ตาม tag
ISR (Incremental Static Regeneration) การสร้างสถิติแบบค่อยเป็นค่อยไป เทคนิคที่อนุญาตให้สร้างและอัปเดตหน้าเว็บแบบ static แบบเรียลไทม์โดยไม่ต้อง rebuild ทั้งหมด
Cache-Control Headers headers ควบคุมแคช HTTP headers ที่ใช้กำหนดว่าใครสามารถเก็บ cache ได้และเก็บได้นานแค่ไหน
Build ID รหัส build identifier ที่ Next.js ใช้ระบุแต่ละ deployment

คำศัพท์ที่สำคัญอื่นๆ

Server-Side Rendering (SSR)

การ render หน้าเว็บบน Server ทุกครั้งที่มี request

Static Site Generation (SSG)

การ generate HTML ที่ build time และ serve ซ้ำ

Client-Side Rendering (CSR)

การ render หน้าเว็บบน Browser โดยใช้ JavaScript

Cache Hit

เมื่อข้อมูลที่ต้องการพบใน cache (สำเร็จ)

Cache Miss

เมื่อข้อมูลที่ต้องการไม่พบใน cache (ต้อง fetch ใหม่)

Time-To-Live (TTL)

ระยะเวลาที่ข้อมูลใน cache ถูกใช้งานได้ก่อนต้อง revalidate