目次11

2026 追記: 旧 VuePress ブログから移植した記事。VuePress 1.x も vuepress-plugin-sitemap もメンテはほぼ停止しており、新規構築の参考にはならない(要出典)。当時の image sitemap / 画像ディレクトリ設計の考え方として残している。コード片はすべて 2021 年時点の VuePress 1.x 前提。

VuePress 1.x で個人ブログを組んでいた頃の、画像配置と sitemap 設定をまとめた記録。Markdown 記事と同じフォルダ構造で画像を管理し、favicon は config.js の head に直書きし、sitemap は vuepress-plugin-sitemap で出力する、という当時の定番構成をそのまま残している。

この記事で扱うのは「画像ディレクトリの切り方」「favicon の指定」「sitemap プラグインの設定」「robots.txt の配置」の 4 点。大きな枠としての VuePress のディレクトリ構成は VuePress/blog のディレクトリ構成 で別に書いている。

結論 — public 直下と public/resources を分ける

短い答え: 画像と sitemap 関連のファイルは、配信ルートに直接出すものと、記事ごとに整理するものを物理的に分ける。

VuePress 1.x は src/.vuepress/public 配下を dist のルートへそのままコピーする。つまり public 直下に置いたファイルは、ビルド後にサイト直下から配信される。favicon・robots.txt・sitemap.xml のように「ルート直下にあること自体に意味があるファイル」は public 直下に、記事画像のように「記事ごとに整理したいファイル」は public/resources 配下に分ける、という切り方にしている。

理由は、両者を同じ階層に置くとファイル数が増えたときに見通しが落ちるから。public 直下に何百枚も記事画像を置くと、favicon が画像群に埋もれてしまう。

対象ファイル

短い答え: 今回触るのは src/.vuepress 配下の public フォルダと config.js の 2 か所。

具体的には以下の構成。

src
└─ .vuepress
   ├─ public
   │  ├─ resources
   │  │  └─ category
   │  │     └─ page
   │  │        └─ img.jpg
   │  ├─ top.jpg
   │  ├─ favicon.ico
   │  ├─ favicon.png
   │  └─ robots.txt
   └─ config.js

注意: より大きいスコープでのディレクトリ全体像は VuePress/blog のディレクトリ構成 を参照。

なぜ image sitemap を整理するか

短い答え: 画像の所在をクローラに伝えるためと、執筆側で画像を見失わないため。

image sitemap は sitemap.xml の中で各 URL に対して「このページにこの画像がある」を並べる拡張仕様。普通の sitemap はページ URL の一覧だが、image sitemap は画像 URL までクローラに渡せる(要出典 / Google の image sitemap 仕様参照)。これにより画像検索からの流入経路が増える、というのが理屈上の利点。

ただし VuePress 1.x の vuepress-plugin-sitemap 2.x は、標準では image エントリを sitemap に出力しない。本記事の構成は「画像が正しく配信され、ページ単位の sitemap が出ている」状態までで、image エントリ込みの厳密な image sitemap はカバーしていない。

比較: image sitemap と通常 sitemap

観点通常 sitemap (sitemap.xml)image sitemap
伝える対象ページ URL の一覧各ページ URL + そのページに含まれる画像
Google への提供方法Search Console / robots.txt同左(拡張要素として埋め込む)
vuepress-plugin-sitemap 2.x の対応標準で生成標準では非対応(要出典)
個人ブログでの効きやすさ必須レベル画像検索流入を狙うなら追加で検討

理由として、画像検索からの流入は写真メディアでは効くが、テキスト主体の技術ブログでは大きく動かないことが多い。最初は通常 sitemap だけ整え、画像検索からの流入が見えてきた段階で image sitemap に踏み込む順序で十分だった。

記事画像のディレクトリを決める

短い答え: src/.vuepress/public/resources 配下に、記事ごとのフォルダを切って画像を置く。

src
└─ .vuepress
   └─ public
      └─ resources
         └─ category
            └─ page
               └─ img.jpg

理由は、Markdown 記事のフォルダ構成と画像のフォルダ構成を 1 対 1 に揃えると、後で記事を移動・削除したときに画像の所在を辿りやすいから。記事 1 本に画像が複数枚入るケースが多いので、ページ単位でフォルダを切っている。

注意: フォルダ名は記事のスラッグと一致させる。記事側のリネームと画像フォルダのリネームを別タイミングでやるとリンク切れが起きる。

共通画像と favicon を public 直下に置く

短い答え: トップ画像・プロフィール画像・favicon は src/.vuepress/public 直下にまとめる。

src
└─ .vuepress
   └─ public
      ├─ favicon.ico
      └─ favicon.png

理由は、まだ共通画像が 2〜3 枚しか無く、フォルダを切るほどでもない規模だから。共通画像が増えてきたら以下のように public/resources/common を切って引っ越す予定にしていた。

src
└─ .vuepress
   └─ public
      └─ resources
         ├─ category
         └─ common
            └─ img.jpg

favicon は VuePress に index.html が無いため、config.js の head 配列に link タグを書いて読ませる。

module.exports = {
  head: [
    ["link", { rel: "icon", href: "/resources/favicon.ico" }],
  ],
}

PWA 化を狙うなら ico とは別に PNG の favicon を用意して、同じ階層に置いておく。当時はサイトの PWA 対応が中途半端だったため、その辺りの記述は省いている。

注意: head の指定はビルド後の HTML すべてに反映される。href のパスを 1 階層間違えるとサイト全体で favicon が読まれなくなる。

vuepress-plugin-sitemap を入れて hostname を設定する

短い答え: vuepress-plugin-sitemap を plugins 配列に追加し、hostname と exclude を指定するだけで sitemap.xml が自動生成される。

当サイトは vuepress-plugin-sitemap を使用していた(記事執筆時は vuepress-plugin-sitemap: ^2.3.1)。設定は config.js で完結する。

module.exports = {
  plugins: [
    [
      "sitemap",
      {
        hostname: "https://xxxx",
        exclude: "https://xxxx/404.html"
      },
    ]
  ],
}

理由として、hostname は sitemap 内の URL 組み立てに使われるため必須。exclude は 404 ページのようにクロール対象から外したい URL を書く。それ以外の細かいオプションは個人ブログ規模では触らなくても困らなかった。

注意: vuepress-plugin-sitemap 2.x は VuePress 1.x 向け。VuePress 2.x 系を使う場合は別パッケージになっており、設定 API も別物(要出典)。

robots.txt を public 直下に置く

短い答え: robots.txt は 必ず src/.vuepress/public 直下に置く。

src
└─ .vuepress
   ├─ public
   │  └─ robots.txt
   └─ config.js

理由は、VuePress では public 配下が dist のルートにコピーされる仕様だから。サブフォルダに置いてもルート直下から配信されず、クローラからは「robots.txt が無いサイト」として扱われる。

中身は他のフレームワークと同じで構わない。

User-agent : *
Disallow :
Sitemap : https://xxxx/sitemap.xml

Sitemap 行を入れておくと、Search Console を介さなくてもクローラが sitemap.xml の場所を拾える。

注意: Disallow を空のままにしているのは「全部許可」のつもりだが、後でステージング環境などを同じ仕組みで公開する場合は、環境ごとに robots.txt を切り替える運用にしておくと安全。

注意点 — 当時詰まったところ

短い答え: 画像パスのつもりが favicon のパスとぶつかる、sitemap.xml のキャッシュが効きすぎる、の 2 点に当時引っかかった。

  • 画像参照を /img.jpg のように書くと、public 直下にあるファイルとパスがぶつかる。記事画像は /resources/category/page/img.jpg のようにフルパスで書くルールに統一した
  • sitemap.xml は CDN とブラウザの両方でキャッシュされやすく、デプロイ直後に Search Console から見ると古い内容のままになることがある。再ビルド後の URL を再送信して反映を確認する
  • robots.txt と sitemap.xml はビルド後の dist 直下に出力されているかを毎回確認する。public 配下の置き場所をうっかり間違えると、ビルドは通るがルートに出ない、という最も気付きにくい事故になる

よくある質問

Q. image sitemap と普通の sitemap.xml は別物? A. image sitemap は sitemap.xml の中で各 URL に image:image エントリを並べる拡張仕様。普通の sitemap がページの一覧だけなのに対し、image sitemap は「そのページにどんな画像があるか」までクローラに伝える。vuepress-plugin-sitemap 2.x は標準では image エントリを出力しないため、画像も含めて伝えたい場合は別途プラグインで拡張するか、自前で sitemap を生成し直す必要があった。

Q. なぜ public 直下ではなく public/resources を切ったのか? A. public 直下に全部置くと、記事画像・共通画像・favicon・robots.txt が同じ階層に混ざって見通しが悪くなる。public/resources を 1 階層挟むことで、記事画像(resources/カテゴリ/記事名)と公開ルート直下に置く必要があるもの(favicon・robots.txt)を物理的に分けられる。

Q. VuePress 1.x の sitemap プラグインは 2026 年でも使えるか? A. 技術的にはまだ動くが、vuepress-plugin-sitemap も VuePress 1.x 自体もメンテはほぼ止まっている。新規構築なら Astro や Next.js のように sitemap 生成が公式に組み込まれている SSG を選んだ方が短期的にも長期的にも楽(要出典)。既存サイトの保守なら、Node の LTS とプラグインのバージョンを固定して延命する形が現実的。

Q. robots.txt を public 直下以外に置くとどうなる? A. ビルド後の dist 配下に robots.txt が出力されないので、ルート配信されず、クローラからは「robots.txt が無いサイト」として扱われる。VuePress では public 配下が dist のルートにコピーされる仕様のため、robots.txt を sitemap.xml と同じ階層で配信したいなら public 直下が唯一の置き場所になる。

まとめ

VuePress 1.x の image / sitemap 周りの初期設定は、public 直下と public/resources の役割を分け、favicon を config.js の head で読ませ、vuepress-plugin-sitemap で hostname と exclude を指定し、robots.txt を public 直下に置くだけで一通り回る。

設定そのものは小さいが、画像ディレクトリの切り方は記事数が増えてから直すと痛みが大きい。最初に「記事画像と共通画像は別の階層」という線だけ引いておくと、後で困りにくい。

2026 年現在の VuePress 1.x は新規構築の選択肢としては勧めにくいので、この記事は当時の構成思想を Astro などと比較するための資料として残している。