これまでの Next.js ブログサイト構築シリーズをまとめたページは以下の通りです。
前回の第9回では、
Markdown のフロントマターから description と ogImage を自動で読み込んで、
記事ごとに SEO タグと OGP タグを設定する仕組みを構築しました。
「記事を書いた。でも公開するまでにやることが多すぎる。」
そう感じている方に向けて、今回はその問題を一気に解決します。
これまでの作業で、ブログとしての基本的な機能はひと通り揃いました。
しかし、まだ2つの課題が残っています。
1つ目は、開発モード(npm run dev )で動いているため、
本番環境としては不適切な状態であること。
2つ目は、記事を追加するたびに手作業が多く、
快適な執筆・公開のフローが整っていないことです。
この記事では、Next.js のブログを本番環境として静的サイト化して公開し、
Obsidian で書いた記事をバッチファイルのダブルクリック1回で
自動デプロイできる仕組みを構築する手順を解説します。
この記事を読むと、以下のことができるようになります。
- Next.js を静的書き出しモードでビルドして本番公開できる
- VPS の公開用フォルダにセキュリティを考慮した権限設定ができる
- Windows の WSL を使って rsync による差分転送が使えるようになる
- Obsidian で記事を書いてダブルクリックするだけで公開できるようになる
静的書き出しを選ぶ理由
これまでは npm run dev という開発モードで Next.js を動かし、Nginx のリバースプロキシ(localhost:3000 への転送)で外部公開していました。
この状態では、アクセスのたびに Node.js が裏側で動いてページを生成しています。
開発には便利ですが、本番環境として使うには以下の問題があります。
- Node.js のプロセスが常時起動し続ける必要がある
- プロセスが落ちるとサイトが見えなくなる
- 開発モードはパフォーマンスが最適化されていない
一方、今回採用する静的書き出し(SSG:Static Site Generation)では、npm run build を実行することですべてのページをあらかじめ HTML ファイルとして書き出します。
Nginx はその静的ファイルを直接返すだけになるため、Node.js のプロセスを常時起動する必要がなくなります。
第1回の記事で「静的サイトに移行する理由」として挙げた「表示速度の向上」と「セキュリティの強化」が、この時点でようやく本当の意味で実現されます。
ここまで進めてきて、ようやくObsidianで作成した記事をほぼ自動で公開することと、サイトの表示速度の向上ができるようになりました。
next.config.ts を静的書き出し用に設定する
ここからは VPS 上での作業です。VPS に SSH 接続して、Next.js の設定ファイルを編集します。
nano はターミナル上で使えるテキストエディタです。Ctrl + O で保存、Ctrl + X で終了します。
nano ~/example-blog/next.config.ts
中身をすべて削除して、以下のコードにまるごと書き換えます。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// 静的ファイルとして書き出す設定
output: 'export',
};
export default nextConfig;
output: 'export' の1行を追加するだけです。
これまで設定していた allowedDevOrigins は開発モード専用の設定です。
静的書き出しモードでは不要になるため、削除してしまって問題ありません。
generateStaticParams() 関数を追加する
静的書き出しモードでビルドを実行すると、動的ルート(/posts/[id])のページでエラーが発生します。
Error: Page "/posts/[id]" is missing "generateStaticParams()"
so it cannot be used with "output: export" config.
これは Next.js が「どのページを書き出せばよいか」を事前に知る必要があるためです。
動的ルートとは、記事ファイルのファイル名(id)によって URL が変わる仕組みのことです。
静的書き出しの場合、Next.js はあらかじめすべての URL を把握してから HTML を生成する必要があるため、generateStaticParams() という「書き出すページの一覧を教える関数」を追加する必要があります。
個別記事ページのファイルを開きます。
nano ~/example-blog/app/posts/\[id\]/page.tsx
まず、ファイルの一番上にある import 文に getSortedPostsData を追加します。
import { getPostData, getSortedPostsData } from '../../../lib/posts';
次に、import 文の直下に以下の関数を追記します。
// 静的書き出し用:書き出すページの一覧をNext.jsに教える関数
export function generateStaticParams() {
const posts = getSortedPostsData();
return posts.map((post) => ({
id: post.id,
}));
}
この関数が posts フォルダ内の Markdown ファイルの一覧を取得し、それぞれのファイル名(id)を Next.js に渡すことで、対応する HTML ページが自動生成されます。
ビルドを実行して静的ファイルを生成する
設定が整ったので、ビルドを実行します。
cd ~/example-blog
npm run build
npm run build は Next.js の本番用ビルドコマンドです。posts フォルダ内の Markdown ファイルをすべて読み込み、HTML ファイルとして書き出します。
成功すると以下のような出力が表示されます。
✓ Generating static pages using 2 workers (7/7)
Route (app)
├ ○ /
├ ○ /_not-found
└ ● /posts/[id]
├ /posts/test-1
├ /posts/test-2
└ /posts/sample-post
posts フォルダ内に保存されている記事ファイルが、それぞれ個別の HTML ページとして生成されています。
out フォルダとは、ビルドによって生成された静的ファイル(HTML・CSS・JavaScript など)が書き出される出力先フォルダです。
生成されたファイルはすべて ~/example-blog/out フォルダに格納されます。
公開用フォルダを作成してセキュリティを設定する
Nginx が静的ファイルを配信するための専用フォルダを作成します。
ホームディレクトリ(~/)に直接ファイルを置くと、Nginx がファイルを読めるようにするためにホームディレクトリへのアクセス権を緩める必要が生じます。
これはセキュリティ上のリスクを高める可能性があるため、Web サーバーの公開用フォルダとして一般的に使われる /var/www/ 以下に専用のフォルダを作成します。
フォルダを作成する
sudo mkdir -p /var/www/next.example.com
mkdir は新しいフォルダを作成するコマンドです。-p オプションをつけると、途中のフォルダが存在しない場合でも一括で作成してくれます。
フォルダ名はサブドメイン名に合わせて付けると、複数のサイトを管理するときに区別しやすくなります。
所有者とグループを確認・設定する
まず、現在のログインユーザーが Nginx のグループ(www-data)に所属しているか確認します。
groups ユーザー名
以下のように www-data が含まれていれば問題ありません。
ユーザー名 : ユーザー名 sudo www-data users
表示されていない場合は、以下のコマンドでグループに追加します。
sudo usermod -aG www-data ユーザー名
続いて、フォルダの所有者とグループを設定します。
sudo chown ユーザー名:www-data /var/www/next.example.com
chown はフォルダの所有者を変更するコマンドです。ユーザー名:www-data と指定することで、「所有者はログインユーザー、グループは Nginx が使う www-data」という構成になります。
アクセス権限を設定する
sudo chmod 750 /var/www/next.example.com
chmod はファイルやフォルダのアクセス権限を変更するコマンドです。750 という数字は以下の意味を持ちます。
7(所有者):読み取り・書き込み・実行すべて可能5(グループwww-data):読み取りと実行のみ可能0(その他):一切アクセス不可
この設定により、Nginx はファイルを読み取れる一方で、第三者(その他のユーザー)は一切アクセスできない最も安全な構成になります。
out フォルダの中身を公開用フォルダにコピーする
ビルドで生成した静的ファイルを公開用フォルダにコピーします。
rsync -av --delete ~/example-blog/out/ /var/www/next.example.com/
rsync は高速なファイル同期コマンドです。各オプションの意味は以下の通りです。
-a:ファイルの属性(権限・タイムスタンプなど)を保持してコピーする-v:コピーしたファイルを画面に表示する(verbose の略)--delete:コピー元にないファイルをコピー先から削除する
Nginx の設定を静的ファイル配信用に変更する
Nginx の設定を、リバースプロキシから静的ファイルの直接配信に切り替えます。
sudo nano /etc/nginx/sites-available/next.example.com.conf
中身をすべて削除して、以下のコードにまるごと書き換えます。next.example.com の部分はご自身のサブドメインに置き換えてください。
server {
listen 80;
server_name next.example.com;
# HTTP通信をHTTPSに自動転送(リダイレクト)する安全設定
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name next.example.com;
# SSL証明書の場所を指定
ssl_certificate /etc/letsencrypt/live/next.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/next.example.com/privkey.pem;
# サーバーのバージョン情報を非表示にするセキュリティ設定
server_tokens off;
# 静的ファイルを直接返す設定
root /var/www/next.example.com;
index index.html;
location / {
try_files $uri $uri.html $uri/ =404;
}
# 404ページの設定
error_page 404 /404.html;
}
ここで追加した server_tokens off; は、Nginx のバージョン情報を外部に公開しないためのセキュリティ設定です。
バージョン情報が見えると、攻撃者がそのバージョンの脆弱性を調べて攻撃してくる可能性があります。
try_files $uri $uri.html $uri/ =404; は、リクエストされた URL に対応するファイルを探し、見つからなければ 404 エラーページを返すという動作を定義しています。
保存して終了したら、設定の文法チェックと再起動を行います。
sudo nginx -t
sudo systemctl restart nginx
nginx -t は設定ファイルの文法チェックを行うコマンドです。syntax is ok と test is successful が表示されれば問題ありません。
ブラウザでサイトにアクセスして、ページが正しく表示されることを確認します。
これで Nginx の設定変更は完了です。次は SSL 証明書の自動更新確認に進みます。
SSL 証明書の自動更新を確認する
本番環境として運用していくにあたって、SSL 証明書の自動更新が正しく設定されているかを確認します。
証明書の有効期限が切れると HTTPS が使えなくなり、ブラウザに警告が表示されてしまいます。
以下のコマンドで自動更新のシミュレーションを実行します。
sudo certbot renew --dry-run
--dry-run は「実際には更新せず、テストだけ行う」オプションです。
本番環境に影響を与えることなく、自動更新の設定が正しいかどうかを確認できます。
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/next.example.com/fullchain.pem (success)
このように success と表示されれば、自動更新は正常に動作しています。
なお、確認の際に使っていないドメインの証明書がエラーになっている場合は、以下のコマンドで削除できます。
sudo certbot delete --cert-name 削除するドメイン名
削除後は再度 --dry-run でエラーがなくなったことを確認してください。
これで SSL 証明書の自動更新確認は完了です。続いて VPS 側のデプロイスクリプトを作成します。
VPS 側にデプロイスクリプトを作成する
記事を追加・更新するたびに「ビルド → 公開フォルダへコピー」という作業を手動で行うのは手間がかかります。
この一連の作業を自動化するシェルスクリプトを VPS 上に作成します。
シェルスクリプトとは、複数のコマンドをまとめて1つのファイルに書いておき、一度に実行できるようにしたプログラムのことです。
nano ~/deploy.sh
以下の内容を貼り付けます。
#!/bin/bash
echo "=== ビルドを開始します ==="
cd ~/example-blog
npm run build
echo "=== 公開フォルダにコピーします ==="
rsync -av --delete ~/example-blog/out/ /var/www/next.example.com/
echo "=== デプロイ完了です ==="
#!/bin/bash は「このファイルを bash というシェルで実行してください」という意味の宣言文です。
シェルスクリプトの1行目には必ずこの記述を入れます。
保存して終了したら、このファイルをコマンドとして実行できるように権限を付与します。
chmod +x ~/deploy.sh
chmod +x は「このファイルを実行可能にする」設定です。
これをしないとスクリプトをコマンドとして呼び出すことができません。
このスクリプトは後ほど Windows 側から SSH 経由で自動的に呼び出します。
ここまでで VPS 側の準備はすべて完了です。
Windows に WSL をインストールして rsync 環境を構築する
ここからは作業場所が VPS から Windows パソコンに切り替わります。
Obsidian で書いた記事を VPS に転送するために、Windows 側に rsync の実行環境を整えます。
rsync は Linux や macOS では標準で使えますが、Windows には標準で含まれていません。
そこで活用するのが WSL(Windows Subsystem for Linux)です。
WSL を使うと Windows 上で本物の Linux 環境が使えるようになります。
Microsoft が公式に提供している機能で、追加のソフトウェアを購入する必要もなく、安全に使用できます。
WSL をインストールする
スタートメニューで「PowerShell」または「コマンドプロンプト」を右クリックして、「管理者として実行」を選択し、以下のコマンドを実行します。
wsl --install
インストール完了後、パソコンを再起動します。
再起動後に Ubuntu が自動でインストールされない場合は、管理者として開いたコマンドプロンプトで以下を実行します。
wsl --install -d Ubuntu
インストールが完了すると初期設定画面が表示されます。
Ubuntu 用のユーザー名とパスワードを設定してください。
Windows のユーザー名やパスワードとは別物で、Ubuntu の中だけで使用されます。
rsync を確認する
Ubuntu のターミナルで rsync が使えるか確認します。
sudo apt update && sudo apt install -y rsync
Ubuntu には標準で rsync が含まれているため、多くの場合は以下のメッセージが表示されて追加インストールは不要です。
rsync is already the newest version (3.x.x)
SSH 鍵を WSL にコピーする
Windows から VPS への SSH 接続に使っている秘密鍵を、WSL の Ubuntu からも使えるようにコピーします。
WSL 上では Windows の各ドライブに以下のようにアクセスできます。
- C ドライブ →
/mnt/c/ - D ドライブ →
/mnt/d/ - E ドライブ →
/mnt/e/
Ubuntu のターミナルで以下のコマンドを順番に実行します。
mkdir -p ~/.ssh
cp /mnt/c/Users/ユーザー名/.ssh/鍵ファイル名 ~/.ssh/
chmod 600 ~/.ssh/鍵ファイル名
mkdir -p ~/.ssh は SSH 鍵を保存するための .ssh フォルダを作成するコマンドです。
すでに存在する場合はそのまま何も起きません。
cp はファイルをコピーするコマンドです。/mnt/c/Users/ユーザー名/.ssh/鍵ファイル名 の部分はご自身の環境に合わせて変更してください。
chmod 600 は「所有者のみ読み書き可能」という権限設定です。
SSH はセキュリティ上の理由から、権限が広すぎる鍵ファイルを拒否する仕様になっているため、この設定が必要です。
SSH 接続をテストする
以下のコマンドで WSL から VPS に接続できるか確認します。
ssh -i ~/.ssh/鍵ファイル名 -p SSHポート番号 ユーザー名@VPSのIPアドレス
-i は使用する秘密鍵ファイルを指定するオプション、-p は接続するポート番号を指定するオプションです。
デフォルトの SSH ポートは22番ですが、セキュリティ対策として別のポート番号を使っている場合はここで指定します。
初回接続時に Are you sure you want to continue connecting (yes/no)? と表示されたら yes と入力します。
VPS のプロンプトが表示されれば接続成功です。
確認できたら exit と入力して WSL に戻ります。
バッチファイルを作成してワンクリックデプロイを実現する
ここまで準備してきた仕組みをすべてまとめた、ダブルクリックだけで動くバッチファイルを作成します。
バッチファイルとは、Windows 上で複数のコマンドを順番に自動実行できるファイルのことです。
拡張子を .bat にするだけで、ダブルクリックで実行できるようになります。
メモ帳を開いて以下の内容を貼り付けます。VPSのIPアドレス・ユーザー名・鍵ファイル名・SSHポート番号・Obsidian のパスの部分は、ご自身の環境に合わせて変更してください。
@echo off
chcp 65001 > nul
echo === 記事ファイルをVPSに転送しています ===
wsl rsync -av --delete "/mnt/Obsidianの記事フォルダのパス/" "ユーザー名@VPSのIPアドレス:~/example-blog/posts/" -e "ssh -i ~/.ssh/鍵ファイル名 -p SSHポート番号"
echo === VPSでビルドと公開を実行しています ===
wsl ssh -i ~/.ssh/鍵ファイル名 -p SSHポート番号 ユーザー名@VPSのIPアドレス "~/deploy.sh"
echo === 完了しました ===
pause
各コマンドの意味は以下の通りです。
@echo off:バッチファイル自体のコマンド行を画面に表示しないようにするchcp 65001 > nul:文字コードを UTF-8 に設定して日本語の文字化けを防ぐ。> nulはこのコマンド自体の出力を非表示にするという意味wsl rsync -av --delete:WSL 経由で rsync を実行する。--deleteオプションにより、Obsidian から削除した記事がサイトからも自動で削除されるwsl ssh ... "~/deploy.sh":WSL 経由で VPS に SSH 接続し、先ほど作成したデプロイスクリプトを自動実行する
メモ帳の「ファイル」→「名前を付けて保存」を選択して、以下の設定で保存します。
- 保存場所:デスクトップ(任意の場所で構いません)
- ファイル名:
deploy.bat - ファイルの種類:すべてのファイル
動作確認
デスクトップの deploy.bat をダブルクリックします。
黒い画面が開き、以下の順番で処理が自動実行されます。
- Obsidian の記事フォルダから VPS へ差分ファイルのみ転送
- VPS でビルドを実行
- 生成した静的ファイルを公開フォルダへコピー
=== 記事ファイルをVPSに転送しています ===
sending incremental file list
./
記事ファイル名.md
...
=== VPSでビルドと公開を実行しています ===
=== ビルドを開始します ===
...
=== デプロイ完了です ===
=== 完了しました ===
続行するには何かキーを押してください . . .
ダブルクリックから約1〜2分で黒い画面に「完了しました」と表示され、
ブラウザを更新すると新しい記事が反映されています。
WordPress の管理画面を開いて原稿をコピペして投稿ボタンを押す、という作業がまるごと不要になった瞬間です。
Obsidian での記事作成ルール
この仕組みが正しく動作するために、Obsidian での記事作成に1つだけルールがあります。
フロントマターはプロパティ機能で設定する
Obsidian には「プロパティ」という機能があり、ノートの上部に「+ プロパティを追加」という
ボタンが表示されます。ここからタイトル・日付・説明を入力すると、
フロントマターがファイルの先頭から正しく記述されます。
手動で --- を書く場合は、必ずファイルの先頭の1文字目から書き始めてください。
先頭に空行があると、Markdown の区切り線として認識されてしまい、
フロントマターの読み取りに使っている gray-matter というツールが正しく解釈できなくなります。
フロントマターの形式は以下の通りです。
---
title: "記事タイトル"
date: "2026-04-14"
description: "記事の説明文(100〜120文字が目安)"
ogImage: "/images/アイキャッチ画像ファイル名.webp"
---
記事本文をここに書く。
まとめ
今回は、Next.js で構築したブログを静的書き出しモードで本番公開し、Obsidian からダブルクリック1回でデプロイできる環境を構築しました。
output: 'export' の1行を追加してビルドするだけで、Node.js を常時起動しない軽量で安全な静的サイトが完成するというのは、Next.js の大きな強みだと改めて実感しました。
バッチファイルをダブルクリックするだけで記事が公開される体験は、WordPress のエディタに記事をコピペしていた頃とは比べものにならないほど快適です。
この仕組みが整ってようやく、Obsidian で書いてそのままブログに公開するという当初の目標が現実になりました。
ただし現時点では、画像ファイルの自動転送と、Obsidian の画像記法(![[image.webp]])を
Next.js 用に自動変換する仕組みがまだ整っていません。
次回は、この画像対応を実装して、完全な自動化フローを完成させます。



コメント