跳至主要內容

    SSR

    SSR 在請求時由伺服器回傳完整 HTML,讓爬蟲與使用者更快看到內容,提升可索引性與首屏體感。

    定義

    SSR(Server-Side Rendering)指每次請求時,由伺服器把 React 等框架渲染成 HTML 後再回傳。它能讓『內容』與『meta/schema』在第一時間就可讀,對 JavaScript SEO 非常友善。

    為什麼重要

    • 更快提供可索引內容,不必等爬蟲執行 JavaScript
    • 首屏更穩:內容先到,互動再 hydration,改善 LCP
    • 適合需要即時資料或個人化內容的頁面(如用戶儀表板)
    • 社群分享正確:OG tags 在初始 HTML,Facebook/Twitter 可正確抓取
    • 支援 AI 引擎索引:ChatGPT/Perplexity 的爬蟲渲染能力有限
    • 降低 Googlebot render queue 依賴:頁面立即可讀
    • 提供一致的 SEO 體驗:不論用戶或爬蟲都看到相同內容

    怎麼做(實作重點)

    • 確保 SSR 輸出包含完整 head:title/description/canonical/hreflang/schema
    • 避免 hydration mismatch:保持初始 HTML 與 CSR 一致,否則 React 會報錯
    • 用 prerender/SSG 承接可靜態化的頁面,降低 SSR 伺服器成本
    • 設定快取策略:對相同請求使用 stale-while-revalidate 減少渲染次數
    • 監控 TTFB:SSR 增加伺服器處理時間,需優化資料取得速度
    • 處理錯誤情況:確保 404/500 頁面也正確 SSR 並回傳對應狀態碼
    • 使用 streaming SSR:React 18+ 支援逐步傳送 HTML,改善首屏體驗

    範例

    typescript
    // pages/product/[id].tsx
    import { GetServerSideProps } from 'next';
    import Head from 'next/head';
    
    interface ProductProps {
      product: { id: string; name: string; price: number };
    }
    
    export default function ProductPage({ product }: ProductProps) {
      return (
        <>
          <Head>
            <title>{product.name} | My Store</title>
            <meta name="description" content={`Buy ${product.name} for ${product.price}`} />
            <link rel="canonical" href={`https://mystore.com/product/${product.id}`} />
          </Head>
          <h1>{product.name}</h1>
          <p>Price: ${product.price}</p>
        </>
      );
    }
    
    export const getServerSideProps: GetServerSideProps = async ({ params, res }) => {
      const product = await fetchProduct(params?.id as string);
      if (!product) {
        return { notFound: true }; // 回傳 404
      }
      // 設定快取(CDN 可用)
      res.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate=300');
      return { props: { product } };
    };
    typescript
    // app/routes/blog.$slug.tsx
    import { json, LoaderFunctionArgs, MetaFunction } from '@remix-run/node';
    import { useLoaderData } from '@remix-run/react';
    
    export const loader = async ({ params }: LoaderFunctionArgs) => {
      const post = await getPost(params.slug!);
      if (!post) {
        throw new Response('Not Found', { status: 404 });
      }
      return json(post, {
        headers: { 'Cache-Control': 's-maxage=3600' }
      });
    };
    
    export const meta: MetaFunction<typeof loader> = ({ data }) => {
      if (!data) return [{ title: 'Not Found' }];
      return [
        { title: data.title },
        { name: 'description', content: data.excerpt },
        { property: 'og:title', content: data.title },
      ];
    };
    
    export default function BlogPost() {
      const post = useLoaderData<typeof loader>();
      return <article><h1>{post.title}</h1><div>{post.content}</div></article>;
    }

    相關連結

    常見問題

    關於這個詞彙的常見問答。

    回到詞彙表