Skip to content

VitePressブログに施したSEO対策まとめ

公開日: 2026-03-01

VitePressで構築した当ブログのSEO評価を確認したところ、7つの改善項目が見つかりました。本記事では、それぞれの対策内容と実装方法をまとめます。

変更ファイルはたった3つで、ほとんどが config.js への追記だけで完結します。なお、以下のコード例ではサイト名・著者名などをプレースホルダとしています。実際の値はご自身のサイトに合わせて置き換えてください。

#対策変更ファイル
1HTML言語属性の設定config.js
2サイトマップの自動生成config.js
3robots.txt の作成docs/public/robots.txt(新規)
4OGPタグの動的生成config.js
5Twitterカードタグの動的生成config.js
6canonical URL + JSON-LD構造化データconfig.js
7RSSフィードの生成config.js + package.json

1. HTML言語属性の設定

HTMLの <html> タグにはページの言語を示す lang 属性が必要です。これが未設定だと、検索エンジンがページの言語を正しく判定できません。

VitePressでは defineConfiglang を指定するだけで反映されます。

js
export default defineConfig({
  lang: 'ja',
})

ビルド後のHTMLに <html lang="ja"> が出力されます。

2. サイトマップの自動生成

サイトマップは、検索エンジンのクローラーにサイト内のページ一覧を伝えるXMLファイルです。VitePress v1.x には組み込みのサイトマップ生成機能があるため、hostname を指定するだけで sitemap.xml が自動生成されます。

js
export default defineConfig({
  sitemap: {
    hostname: 'https://garookie.com'
  },
})

3. robots.txt の作成

robots.txt はクローラーに対して、サイト内のどのページをクロールしてよいかを指示するファイルです。サイトマップの場所もここで明示できます。

docs/public/robots.txt に配置すると、ビルド時にそのまま出力ディレクトリへコピーされます。

User-agent: *
Allow: /

Sitemap: https://garookie.com/sitemap.xml

4. OGPタグの動的生成

OGP(Open Graph Protocol)タグを設定すると、SNSやメッセージアプリでURLを共有した際にタイトル・説明文・サイト名などがリッチに表示されます。

VitePressの transformHead フックを使うと、ページごとのfrontmatterから動的にmetaタグを生成できます。

js
export default defineConfig({
  transformHead({ pageData }) {
    const head = []
    // relativePathからページURLを組み立てる
    const pagePath = pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2')
    const pageUrl = `${hostname}/${pagePath}`
    const title = pageData.frontmatter.title || 'サイト名'
    const description = pageData.frontmatter.description || 'サイト説明'
    const isPost = pageData.relativePath.startsWith('posts/') && pageData.frontmatter.date

    head.push(['meta', { property: 'og:type', content: isPost ? 'article' : 'website' }])
    head.push(['meta', { property: 'og:title', content: title }])
    head.push(['meta', { property: 'og:description', content: description }])
    head.push(['meta', { property: 'og:url', content: pageUrl }])
    head.push(['meta', { property: 'og:site_name', content: 'サイト名' }])
    head.push(['meta', { property: 'og:locale', content: 'ja_JP' }])

    return head
  }
})

ポイントは、記事ページでは og:typearticle に、それ以外では website にしている点です。

5. Twitterカードタグの動的生成

Twitterカードタグを設定すると、X(旧Twitter)でURLを共有した際にカード形式で表示されます。OGPと同じく transformHead 内で生成します。

js
head.push(['meta', { name: 'twitter:card', content: 'summary' }])
head.push(['meta', { name: 'twitter:title', content: title }])
head.push(['meta', { name: 'twitter:description', content: description }])

6. canonical URL + JSON-LD構造化データ

canonical URL

canonical URLは「このページの正規URLはこれです」と検索エンジンに明示するためのタグです。重複コンテンツの評価分散を防ぎます。

以下のコードはすべて上記セクション4の transformHead 内での処理です。pageUrl の組み立てについてはセクション4を参照してください。

js
head.push(['link', { rel: 'canonical', href: pageUrl }])

JSON-LD構造化データ

JSON-LDは検索エンジンにページの意味(記事なのか、サイトなのか等)を構造化データとして伝える仕組みです。Googleの検索結果でリッチリザルトとして表示される可能性が高まります。

記事ページには BlogPosting、それ以外には WebSite スキーマを設定しました。

js
// 記事ページの場合
const jsonLd = {
  '@context': 'https://schema.org',
  '@type': 'BlogPosting',
  headline: title,
  description,
  url: pageUrl,
  datePublished: pageData.frontmatter.date,
  author: { '@type': 'Person', name: '著者名' },
  publisher: { '@type': 'Organization', name: 'サイト名' }
}
head.push(['script', { type: 'application/ld+json' }, JSON.stringify(jsonLd)])

7. RSSフィードの生成

RSSフィードを配信すると、フィードリーダーの利用者が新着記事を自動で受け取れるようになります。また、一部の検索エンジンもRSSフィードを参照します。

まず feed パッケージをインストールします。

bash
npm install -D feed

VitePressの buildEnd フックで、ビルド完了時に feed.xml を生成します。

js
import { createContentLoader } from 'vitepress'
import { writeFileSync } from 'fs'
import path from 'path'
import { Feed } from 'feed'

export default defineConfig({
  // headにRSS自動検出リンクを追加
  head: [
    ['link', { rel: 'alternate', type: 'application/rss+xml', title: 'サイト名', href: '/feed.xml' }]
  ],

  async buildEnd(siteConfig) {
    const feed = new Feed({
      title: 'サイト名',
      description: 'サイト説明',
      id: hostname,
      link: hostname,
      language: 'ja',
    })

    const posts = await createContentLoader('posts/*.md', {
      excerpt: true,
      render: true
    }).load()

    posts
      .filter(p => p.frontmatter.date)
      .sort((a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date))
      .forEach(post => {
        feed.addItem({
          title: post.frontmatter.title,
          id: `${hostname}${post.url}`,
          link: `${hostname}${post.url}`,
          description: post.frontmatter.description || '',
          date: new Date(post.frontmatter.date)
        })
      })

    writeFileSync(path.join(siteConfig.outDir, 'feed.xml'), feed.rss2())
  }
})

head<link rel="alternate"> を追加することで、ブラウザやフィードリーダーがRSSフィードを自動検出できるようにもなります。


まとめ

今回の対策は、変更ファイル3つ(config.js / robots.txt / package.json)だけで完結しました。VitePressは sitemap の組み込み機能や transformHead / buildEnd といったフックが用意されているため、プラグインを追加することなくSEO対策を実装できるのが良いところです。

対策効果
lang属性検索エンジンが言語を正しく認識
サイトマップクローラーが全ページを効率的に巡回
robots.txtクロールの許可とサイトマップの所在を明示
OGPタグSNSシェア時にリッチ表示
TwitterカードXでのシェア時にカード表示
canonical + JSON-LD重複防止 + リッチリザルト対応
RSSフィードフィードリーダーでの新着記事配信