Website Deployment
- Published time
前言#
把個人網站寫完只是第一步。真正讓它「存在」於網路上,還需要兩件事:部署到雲端,以及讓搜尋引擎找得到。
第二件事很容易被忽略。網站部署完、網域也綁好了,打開瀏覽器輸入網址一切正常,但去 Google 搜尋自己的名字卻什麼都找不到——因為搜尋引擎根本不知道你的網站存在。
這篇會走過完整流程:從 Vercel 部署、Namecheap 網域設定,到 sitemap、robots.txt 與 next-seo 的設定。
雲端部署#
推薦使用 Vercel 當作 server。它是 Next.js 的開發團隊自己做的平台,所以對 Next.js 的支援最完整,接上 GitHub repo 之後每次 push 就會自動部署,個人專案的用量也在免費額度內。
網域註冊#
使用 Vercel 部署其實就會提供一個 .vercel.app 的網址,但我希望使用自己喜歡的域名,所以到 Namecheap 上購買。
所謂域名其實就只是一個字串,它要被 DNS 解析成 IP 之後才有意義。所以在 Namecheap 的 Advanced DNS 設定中,要讓域名指向 Vercel 的伺服器 IP:
但如果只有這樣,明顯是不夠的。因為有這麼多專案在 Vercel 上運行,同一個 IP 對應了這麼多網域,Vercel 怎麼知道進來的請求是要找哪一個?
答案是 反向代理(reverse proxy):Vercel 會讀取 HTTP 請求裡的 Host 標頭,來判斷這個請求屬於哪一個專案。所以我們還需要在 Vercel 這邊也綁定域名,讓它知道這個 Host 該對應到誰:
簡單來說,整條路徑是這樣:
Copied!使用者輸入 example.com ↓ 瀏覽器詢問 DNS → 找到 A Record / CNAME ↓ DNS 回覆:IP = 76.76.21.21 (Vercel) ↓ 瀏覽器發送 HTTP 請求到 Vercel IP ↓ Vercel 依照請求中的網域 → 對應到你的專案 ↓ Vercel 回傳你的網站內容 (HTML/CSS/JS)
兩邊都設定好之後,就可以打開瀏覽器輸入域名,看到你的網站了。
但如果去 Google 上搜尋,會發現不管怎麼找都找不到。這是因為搜尋引擎還沒有發現和索引你的網站——DNS 只解決了「知道網址的人找得到你」,剩下的要靠 SEO。
搜尋引擎優化(SEO)#
要讓你的 Vercel 部署的 Next.js 網站被 Google 等搜尋引擎找到,需要完成以下步驟。
建立並提交 Sitemap#
Sitemap(網站地圖) 是一份告訴搜尋引擎「我的網站有哪些頁面」的清單。沒有它,爬蟲只能靠連結一頁一頁摸索,沒被連到的頁面就可能永遠不會被發現。
自動生成 Sitemap(建議)#
使用 next-sitemap 套件:
Copied!npm install next-sitemap
在專案根目錄新增 next-sitemap.config.js:
Copied!module.exports = { siteUrl: 'https://yourdomain.com', generateRobotsTxt: true, };
修改 package.json,讓建置完成後自動生成 sitemap:
Copied!{ "scripts": { "build": "next build", "postbuild": "next-sitemap" } }
用 postbuild 的好處是每次部署都會自動重新產生,新增文章後不用手動更新 sitemap。
設定 robots.txt#
在根目錄的 public/robots.txt:
Copied!User-agent: * Allow: / Sitemap: https://yourdomain.com/sitemap.xml
這表示允許搜尋引擎抓取所有頁面,並且告訴它 Sitemap 的位置。如果前面設了 generateRobotsTxt: true,next-sitemap 會自動幫你產生這個檔案,就不需要手動建立。
在 Google Search Console 驗證網域#
- 前往 Google Search Console
- 新增網站 → 輸入
yourdomain.com - 選擇透過 DNS TXT 記錄驗證,在域名供應商(例如 Namecheap)的 DNS 設定中新增 TXT record:
Copied!Host: @ Value: google-site-verification=xxxxxx TTL: Automatic
DNS 的變更需要時間傳播,如果驗證失敗,等個十幾分鐘再試一次通常就會過。
提交 Sitemap 給 Google#
在 Search Console 的 Sitemaps 頁面,輸入:
Copied!sitemap.xml
送出之後,Google 就會排程來抓取你的網站。
next-seo#
如果只做到這裡,還是會覺得少了什麼:平常我們搜尋關鍵字時,Google 是靠什麼判斷該不該顯示某個網站的?前面的步驟都沒有處理到這件事。
答案是每個頁面的 meta 標籤。一般來說,我們可以在每一個頁面加上 <Head>,像底下這樣:
Copied!import Head from 'next/head'; <Head> <title>Allen Wang | Developer</title> <meta name="description" content="I build trading systems..." /> <meta property="og:title" content="Allen Wang" /> <meta property="og:image" content="/photo.jpg" /> </Head>;
其中 title 和 description 決定了搜尋結果上顯示的標題與摘要,而 og: 開頭的則是 Open Graph 標籤,決定連結被分享到社群平台時預覽卡片長什麼樣子。
但這樣每一個頁面都要寫一次太麻煩了,用 next-seo 可以把它自動化。
全站預設 SEO#
在 _app.js 或 _app.tsx 中設定:
Copied!import { DefaultSeo } from 'next-seo'; export default function MyApp({ Component, pageProps }) { return ( <> <DefaultSeo title="你的網站標題" description="你的網站描述,150 字內" canonical="https://yourdomain.com" openGraph={{ type: 'website', locale: 'zh_TW', url: 'https://yourdomain.com', siteName: '你的網站名稱', }} twitter={{ handle: '@你的帳號', site: '@你的帳號', cardType: 'summary_large_image', }} /> <Component {...pageProps} /> </> ); }
- 這裡定義的 meta / openGraph / twitter 等設定,會套用在所有頁面。
- 如果某一頁沒有額外定義,就直接沿用這些預設值。
單一頁面 SEO#
在需要客製的頁面加上:
Copied!import { NextSeo } from 'next-seo'; export default function HomePage() { return ( <> <NextSeo title="首頁標題" description="首頁描述" canonical="https://yourdomain.com/" openGraph={{ url: 'https://yourdomain.com/', title: '首頁標題', description: '首頁描述', images: [ { url: 'https://yourdomain.com/home-og.jpg', width: 1200, height: 630 }, ], }} /> <main>你的頁面內容</main> </> ); }
canonical 這個欄位值得特別留意:它告訴搜尋引擎「這個頁面的正式網址是哪一個」,避免同一份內容因為有多個網址(例如帶不帶 www、帶不帶查詢參數)而被視為重複內容。
文章頁面(動態內容)#
文章頁除了 meta 之外,還可以用 ArticleJsonLd 產生結構化資料,讓 Google 知道這是一篇文章、作者是誰、什麼時候發布的:
Copied!import { ArticleJsonLd, NextSeo } from 'next-seo'; export default function BlogPost({ post }) { return ( <> <NextSeo title={post.title} description={post.excerpt} canonical={`https://yourdomain.com/blog/${post.slug}`} openGraph={{ type: 'article', title: post.title, description: post.excerpt, article: { publishedTime: post.publishedAt, modifiedTime: post.updatedAt, authors: ['https://yourdomain.com/about'], tags: post.tags, }, images: [{ url: post.ogImage, width: 1200, height: 630 }], }} /> <ArticleJsonLd url={`https://yourdomain.com/blog/${post.slug}`} title={post.title} images={[post.ogImage]} datePublished={post.publishedAt} dateModified={post.updatedAt} authorName="你的名字" description={post.excerpt} /> <article>{/* 文章內容 */}</article> </> ); }
- 頁面層級的設定會覆蓋
DefaultSeo的設定。 - 適合設定:文章標題、文章描述、文章縮圖,以及所有動態生成的內容。
驗證結果#
等待 Google 抓取需要一點時間,通常是幾天而不是幾小時。如果想確認是否已經被索引,可以在 Google 搜尋:
Copied!site:yourdomain.com
成功的話會像這樣:
這個指令只會列出已經被索引的頁面,所以如果什麼都沒出現,代表還沒被收錄,可以回 Search Console 看看是不是有抓取錯誤。
結語#
整個流程可以拆成三層來理解:DNS 讓網址找得到伺服器、反向代理讓伺服器知道要回哪個專案、SEO 讓搜尋引擎知道你存在。
前兩層設定完就一勞永逸,第三層則是持續的工作——每次新增內容,sitemap 和 meta 標籤都要跟著更新,而這也正是為什麼值得一開始就把 next-sitemap 和 next-seo 設定好,讓它自動處理。
這篇的內容大概就這樣囉。
