Billy Tse
HomeRoadmapBlogContact
Playground
Buy me a bug

© 2026 Billy Tse

OnlyFansLinkedInGitHubEmail
Back to Blog
February 1, 2026•10 min read

Next.js Server-Side Rendering 同 SEO:force-dynamic 點樣保持搜尋引擎優化

深入探討 Next.js 三種 rendering 策略(SSG、SSR、CSR)對 SEO 嘅影響,解析 force-dynamic 點樣喺 server-side 生成完整 HTML,確保搜尋引擎可以正確 crawl 你嘅內容

Next.jsSEOWeb Development

做緊 Next.js blog 嘅時候成日有人問:用咗 force-dynamic 會唔會影響 SEO?答案係:唔會! 呢篇文章會詳細解釋點解。

TL;DR

好多人以為 server-side rendering (SSR) 同 client-side rendering (CSR) 一樣會影響 SEO,但其實完全唔同。Next.js 嘅 force-dynamic 係喺 server 度生成完整 HTML,搜尋引擎 crawlers 收到嘅同 static site 一模一樣——只不過生成 HTML 嘅時間唔同而已。

核心重點:

  • ✅ SSR (force-dynamic) 對 SEO 好:Full HTML + metadata + structured data
  • ❌ CSR 對 SEO 差:Empty HTML,要等 JS load 先有內容
  • 🎯 關鍵分別:HTML 喺邊度生成(server vs client)

三種 Rendering 策略比較

Next.js 提供三種主要嘅 rendering 方式,每種對 SEO 嘅影響都唔同:

Rendering TypeSEO點樣運作HTML 生成時間
Static (SSG)✅ 最好Build time 預先生成 HTMLBuild time
Dynamic (SSR) ← 我哋用緊✅ 好每次 request 喺 server 生成 HTMLRequest time (server)
Client-side (CSR)❌ 差HTML 空白,靠 JS load 完先 fetch dataAfter page load (client)

詳細流程對比

Static Site Generation (SSG)

Build Time: └─ Next.js builds all pages └─ Generates complete HTML files └─ Stores in CDN Request Time: └─ CDN serves pre-built HTML └─ Browser/crawler receives full content immediately

優點: 最快、SEO 最好

缺點: 內容靜態,唔適合 dynamic data(例如從 Notion API fetch)

Server-Side Rendering (SSR) — force-dynamic

Build Time: └─ No HTML pre-generated Request Time: └─ Server receives request └─ Server fetches data (e.g., Notion API) └─ Server renders React components to HTML └─ Server sends complete HTML to client └─ Browser/crawler receives full content

優點: Dynamic content + 保持 SEO

缺點: 每次 request 都要 server render(慢少少)

Client-Side Rendering (CSR)

Build Time: └─ Minimal HTML shell Request Time: └─ Server sends empty HTML + JS bundle └─ Browser loads JS └─ JS executes, fetches data └─ React renders content on client └─ Content finally appears

優點: Interactive、減少 server load

缺點: SEO 災難 — Crawlers 見到空白頁面

force-dynamic 點樣運作?

Next.js App Router 入面,force-dynamic 係一個 route segment config,用嚟 force 個 route 做 dynamic rendering。

基本用法

// app/blog/[slug]/page.tsx export const dynamic = 'force-dynamic' export default async function BlogPost({ params }) { // Fetch from Notion API (or any dynamic source) const post = await fetchNotionPage(params.slug) return ( <article> <h1>{post.title}</h1> <div>{post.content}</div> </article> ) }

完整 Request Lifecycle

當 Googlebot 或者用戶訪問你嘅 blog page:

1. Request 到達 Next.js server

GET /blog/nextjs-ssr-seo-force-dynamic

2. Server 執行 page component

  • 因為有 force-dynamic,唔會 cache
  • 執行 fetchNotionPage(slug)
  • 等 API response(Notion data)

3. Server 將 React component render 成 HTML

<!DOCTYPE html> <html> <head> <title>Next.js Server-Side Rendering 同 SEO</title> <meta name="description" content="深入探討..."> <meta property="og:title" content="..."> <script type="application/ld+json"> {"@type": "BlogPosting", "headline": "..."} </script> </head> <body> <article> <h1>Next.js Server-Side Rendering 同 SEO</h1> <p>做緊 Next.js blog 嘅時候...</p> <!-- Full content here! --> </article> </body> </html>

4. Server 返回完整 HTML

  • Status: 200 OK
  • Content-Type: text/html
  • Body: 上面嗰個完整 HTML

5. Crawler/Browser 收到

  • 所有內容已經喺 HTML 入面
  • 唔需要等 JS
  • 唔需要 client-side fetch

點解 SSR 對 SEO 冇影響?

搜尋引擎睇到啲咩?

無論你用 SSG 定 SSR,Google/Bing crawlers 收到嘅都係完整 HTML:

ElementSSGSSR (force-dynamic)CSR
<title> tag✅ Present✅ Present❌ Generic/Empty
<meta> description✅ Full text✅ Full text❌ Missing
OG tags (social)✅ Complete✅ Complete❌ Missing
JSON-LD structured data✅ Embedded✅ Embedded❌ Missing
Content <h1>, <p>, etc.✅ Full content✅ Full content❌ Empty or loading spinner

實際例子:Crawler 收到嘅 HTML

SSR with force-dynamic (✅ Good for SEO)

<!-- Googlebot fetch 嘅結果 --> <!DOCTYPE html> <html lang="zh-HK"> <head> <title>Next.js Server-Side Rendering 同 SEO:force-dynamic 點樣保持搜尋引擎優化 | Billy Tse</title> <meta name="description" content="深入探討 Next.js 三種 rendering 策略..."> <meta property="og:title" content="Next.js Server-Side Rendering 同 SEO"> <meta property="og:description" content="深入探討..."> <meta property="og:image" content="https://example.com/og-image.jpg"> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BlogPosting", "headline": "Next.js Server-Side Rendering 同 SEO", "author": {"@type": "Person", "name": "Billy Tse"}, "datePublished": "2026-02-01" } </script> </head> <body> <article> <h1>Next.js Server-Side Rendering 同 SEO:force-dynamic 點樣保持搜尋引擎優化</h1> <p>做緊 Next.js blog 嘅時候成日有人問:用咗 force-dynamic 會唔會影響 SEO?</p> <!-- 所有內容都喺呢度! --> </article> </body> </html>

CSR (❌ Bad for SEO)

<!-- Googlebot fetch 嘅結果 --> <!DOCTYPE html> <html> <head> <title>My App</title> </head> <body> <div id="root"></div> <script src="/bundle.js"></script> <!-- Content 要等 JS load 同 execute 先會出現! --> </body> </html>

搜尋引擎見到空白 → SEO 災難

關鍵誤解:動態唔等於 Client-Side

好多人混淆咗呢兩個概念:

誤解事實
"Dynamic data 一定要 client-side fetch"❌ 錯!可以 server-side fetch (SSR)
"force-dynamic 會令 SEO 變差"❌ 錯!HTML 一樣係 server 生成
"只有 SSG 先對 SEO 好"❌ 錯!SSR 都一樣好
"SSR 同 CSR 一樣"❌ 完全唔同!SSR = server render, CSR = client render

真正影響 SEO 嘅係:HTML 喺邊度生成?

🎯 核心概念
SEO 睇嘅係:當 crawler request 你個 page,server 返嘅 HTML 入面有冇內容?

  • ✅ Server 生成 HTML (SSG/SSR):有內容 → SEO 好

  • ❌ Client 生成 HTML (CSR):冇內容 → SEO 差

force-dynamic 係 server-side,所以 SEO 完全冇問題!

Next.js Metadata API + force-dynamic

Next.js App Router 提供 Metadata API 嚟生成 SEO tags。即使用咗 force-dynamic,呢啲 metadata 都係 server-side 生成。

動態 Metadata 例子

// app/blog/[slug]/page.tsx import { Metadata } from 'next' export const dynamic = 'force-dynamic' // 👇 Server-side function,每次 request 都會執行 export async function generateMetadata({ params }): Promise<Metadata> { // Fetch dynamic data from Notion const post = await fetchNotionPage(params.slug) return { title: post.title, description: post.description, openGraph: { title: post.title, description: post.description, images: [post.coverImage], type: 'article', publishedTime: post.publishedDate, authors: [post.author], }, twitter: { card: 'summary_large_image', title: post.title, description: post.description, images: [post.coverImage], }, } } export default async function BlogPost({ params }) { const post = await fetchNotionPage(params.slug) return ( <article> <h1>{post.title}</h1> <div dangerouslySetInnerHTML= __html: post.content /> </article> ) }

生成嘅 HTML

<head> <!-- Next.js 自動將 metadata 轉成 HTML tags --> <title>Next.js Server-Side Rendering 同 SEO | Billy Tse Blog</title> <meta name="description" content="深入探討 Next.js 三種 rendering 策略..."> <!-- Open Graph (Facebook, LinkedIn) --> <meta property="og:title" content="Next.js Server-Side Rendering 同 SEO"> <meta property="og:description" content="深入探討..."> <meta property="og:image" content="https://example.com/cover.jpg"> <meta property="og:type" content="article"> <meta property="article:published_time" content="2026-02-01"> <meta property="article:author" content="Billy Tse"> <!-- Twitter Cards --> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="Next.js Server-Side Rendering 同 SEO"> <meta name="twitter:description" content="深入探討..."> <meta name="twitter:image" content="https://example.com/cover.jpg"> </head>

所有呢啲 tags 都係 server-side 生成,crawler 直接見到!

Structured Data (JSON-LD)

除咗 meta tags,你仲可以加 structured data 俾 Google Rich Results。

實現方法

// app/blog/[slug]/page.tsx export default async function BlogPost({ params }) { const post = await fetchNotionPage(params.slug) const jsonLd = { '@context': 'https://schema.org', '@type': 'BlogPosting', headline: post.title, description: post.description, image: post.coverImage, datePublished: post.publishedDate, dateModified: post.modifiedDate, author: { '@type': 'Person', name: post.author, url: 'https://billytse.dev', }, publisher: { '@type': 'Organization', name: 'Billy Tse Blog', logo: { '@type': 'ImageObject', url: 'https://billytse.dev/logo.png', }, }, } return ( <> <script type="application/ld+json" dangerouslySetInnerHTML= __html: JSON.stringify(jsonLd) /> <article> <h1>{post.title}</h1> <div>{post.content}</div> </article> </> ) }

效果

Google 會用呢啲 structured data 顯示 rich snippets:

  • ⭐ 文章評分
  • 📅 發佈日期
  • 👤 作者資訊
  • 🖼️ 預覽圖片
  • ⏱️ 閱讀時間

所有呢啲都係 server-side 生成,SEO 完全保留!

實際效能考量

雖然 SSR 對 SEO 冇影響,但對 performance 有啲考量:

SSG vs SSR 效能

MetricSSGSSR
TTFB (Time to First Byte)~10-50ms (CDN)~200-1000ms (depends on API)
Server Load無(serve static files)每次 request 都要 render
Scalability極好(CDN edge caching)需要 server capacity
Data FreshnessBuild time snapshotReal-time

點樣優化 SSR?

  1. 用 React Cache
import { cache } from 'react' // Cache function results during request const getNotionPage = cache(async (slug: string) => { return await notionAPI.getPage(slug) })
  1. 用 Next.js unstable_cache(跨 request cache)
import { unstable_cache } from 'next/cache' const getCachedPage = unstable_cache( async (slug) => await fetchNotionPage(slug), ['notion-page'], { revalidate: 60 } // Cache 60 seconds )
  1. Partial Prerendering (Next.js 14+)
// app/blog/[slug]/page.tsx export const experimental_ppr = true export default function BlogPost({ params }) { return ( <> {/* Static shell renders immediately */} <Header /> {/* Dynamic content streams in */} <Suspense fallback={<Skeleton />}> <BlogContent slug={params.slug} /> </Suspense> </> ) }

你嘅 Blog 係點樣嘅?

基於你嘅 setup(Next.js + Notion API + force-dynamic),以下係實際情況:

✅ 你有齊所有 SEO 要素

1. Server Components

  • 你嘅 blog page 係 async server component
  • React 喺 server 度 render,唔係 client

2. Complete HTML

// 每次 Googlebot request: Server fetches from Notion API ↓ Server renders React to HTML ↓ Sends complete HTML (title, meta, content, everything!) ↓ Googlebot sees full page ✅

3. Metadata Generated Server-Side

  • <title> tag: ✅
  • <meta name="description">: ✅
  • Open Graph tags: ✅
  • 所有 metadata 都係 server 生成

4. JSON-LD Structured Data

  • 如果你有加 BlogPosting schema
  • 都係 server-side 生成 ✅

🎯 結論

用咗 force-dynamic 對 SEO 完全冇負面影響!

唯一嘅分別:

  • SSG:HTML 喺 build time 生成一次
  • SSR (force-dynamic):HTML 喺 request time 生成

但對搜尋引擎嚟講,兩者收到嘅都係完整 HTML,效果一樣。

驗證方法

想確認你嘅 SSR 對 SEO 有效?試下呢啲工具:

1. View Page Source(最簡單)

喺瀏覽器:

  1. Right-click → "View Page Source"
  2. 檢查 HTML 入面有冇你嘅內容

如果你見到:

<h1>Next.js Server-Side Rendering 同 SEO</h1> <p>做緊 Next.js blog 嘅時候成日有人問...</p>

→ ✅ SSR 運作正常,SEO 冇問題

如果你淨係見到:

<div id="root"></div>

→ ❌ 呢個係 CSR,SEO 有問題

2. Google Search Console

  • 用 URL Inspection Tool
  • Request indexing
  • 睇 "View crawled page"
  • 確認 Google 見到你嘅內容

3. Rich Results Test

  • 去 Google Rich Results Test
  • 輸入你嘅 blog URL
  • 檢查 structured data 有冇 detect 到

4. curl 測試

curl https://your-blog.com/blog/your-post | grep "<title>"

如果 return 到你嘅 title → ✅ Server 有返 HTML

5. Chrome DevTools Network Tab

  • Open DevTools → Network
  • Hard reload (Cmd+Shift+R / Ctrl+Shift+R)
  • Click on the initial document request
  • 睇 Response tab
  • 確認 HTML response 已經有晒內容

常見問題 (FAQ)

Q1: force-dynamic 會唔會令 Google ranking 跌?

A: 唔會。Google 主要睇:

  1. Content quality(內容質素)
  2. User experience(用戶體驗)
  3. Page speed(載入速度)
  4. Mobile-friendliness(手機兼容)

SSR 可能會慢少少(TTFB),但係:

  • HTML 一樣係 complete
  • Content 一樣係 indexable
  • 對 ranking 影響極小

如果你擔心速度,可以用 caching 優化。

Q2: 使唔使改用 SSG?

A: 睇情況:

用 SSG 如果:

  • 內容唔常改(例如每日一次 rebuild 都 OK)
  • 想極致速度(CDN edge caching)
  • 有固定數量嘅 pages(唔係無限)

用 SSR 如果:

  • 需要 real-time data(例如 Notion API 即時更新)
  • Pages 數量太多(build time 會好長)
  • 需要 per-request customization

對你嚟講,如果 Notion 係你嘅 CMS,SSR 可能更合適。

Q3: 可唔可以混合 SSG 同 SSR?

A: 可以!Next.js 支持 per-route configuration:

// app/blog/page.tsx (List page - SSG) export const dynamic = 'force-static' // app/blog/[slug]/page.tsx (Post page - SSR) export const dynamic = 'force-dynamic'

或者用 ISR (Incremental Static Regeneration):

export const revalidate = 3600 // Rebuild every hour

Q4: Server Components 同 SSR 有咩分別?

A: 呢兩個係唔同層面嘅概念:

Server Components:React component 喺 server 執行

SSR:整個 page 喺 server render

喺 Next.js App Router:

  • 所有 components 預設係 Server Components
  • force-dynamic 決定幾時 render (every request vs build time)

你可以有:

  • Server Components + SSG(build time render)
  • Server Components + SSR(request time render)← 你呢個

Q5: 用咗 use client 會唔會影響 SEO?

A: 睇你用喺邊度:

❌ 如果成個 page 都係 client component:

'use client' export default function BlogPost() { const [post, setPost] = useState(null) useEffect(() => { fetch('/api/post').then(r => setPost(r)) }, []) return <div>{post?.content}</div> }

→ SEO 差,因為 content 係 client-side fetch

✅ 如果只係部分 interactive components:

// page.tsx (Server Component) export default async function BlogPost() { const post = await fetchNotionPage() // Server-side return ( <article> <h1>{post.title}</h1> <div>{post.content}</div> <LikeButton /> {/* Client Component */} </article> ) } // LikeButton.tsx 'use client' export default function LikeButton() { const [likes, setLikes] = useState(0) return <button onClick={() => setLikes(l => l + 1)}>Like ({likes})</button> }

→ SEO 好,因為 main content 係 server-side

💡 最佳實踐:

  • Main content = Server Components

  • Interactive UI (buttons, forms) = Client Components

  • 呢樣嘢叫 "partial hydration",兼顧 SEO 同 interactivity

總結

核心重點

  1. force-dynamic 唔會影響 SEO
    • HTML 依然係 server-side 生成
    • Crawlers 收到完整 content
    • Metadata 同 structured data 保留
  2. 真正影響 SEO 嘅係 rendering 位置
    • ✅ Server rendering (SSG/SSR) → 好
    • ❌ Client rendering (CSR) → 差
  3. 你嘅 blog setup 完全 OK
    • Server Components ✅
    • Full HTML ✅
    • Metadata ✅
    • JSON-LD ✅

何時擔心 SEO?

❌ 唔使擔心:

  • 用 force-dynamic
  • 用 Server Components
  • 用 Next.js Metadata API
  • 從 Notion API fetch data (server-side)

⚠️ 要擔心:

  • 用 use client + useEffect fetch data
  • Empty HTML shell
  • JavaScript-only rendering
  • Missing meta tags

下一步建議

如果你想進一步優化:

  1. 加 caching 減少 Notion API calls
  2. 用 Partial Prerendering 平衡速度同 freshness
  3. 加 JSON-LD 爭取 rich snippets
  4. 用 Google Search Console 監察 indexing
  5. 測試 mobile performance(Core Web Vitals)

相關資源

  • 📘 Next.js Rendering Documentation
  • 🔍 Google Search Central - JavaScript SEO
  • 📊 Google Rich Results Test
  • 🎯 Next.js Metadata API
  • ⚡ Web.dev: Rendering on the Web

希望呢篇文章解答咗你對 Next.js SSR 同 SEO 嘅疑問。記住:只要 HTML 係 server 生成,SEO 就冇問題!

Back to all articles
目錄