Astro サイトをサブドメインで新しく公開する

Astro 自宅サーバ DNS 覚書

Roblox Studio での開発日記を、今の nosubject.io とは少し分けて置きたくなった。

新しいドメインを取る方法もあるが、まずは create.nosubject.io のようなサブドメインを作り、別の静的サイトとして公開するのが扱いやすそうだ。

このサイト本体は Hugo で運用しているが、サブドメイン側では Astro を使ってみる。Astro は Markdown や MDX を扱いやすく、コンポーネントを使ったページ作りにも寄せやすいので、Roblox Studio のスクリーンショットや制作ログを置く場所として試しやすい。

この記事では、create.nosubject.io を例にして、Astro サイトを新しく作り、DNS、Nginx、HTTPS、GitHub Actions のビルド設定までつなげる流れをまとめる。

実施した環境

この記事では、次のような環境を前提にしている。

  • 既存サイトは https://nosubject.io/
  • 新しいサイトは https://create.nosubject.io/
  • サブドメイン側の静的サイトジェネレーターは Astro
  • 公開先は Raspberry Pi 5 上の Nginx
  • Nginx は Docker Compose で起動
  • Astro のビルドは GitHub Actions の self-hosted runner で実行
  • DNS はお名前.com Navi で変更できる状態

Astro の公式ドキュメントでは、現在の前提として Node.js v22.12.0 以上が案内されている。Raspberry Pi 5 の runner 側でも、node --version で条件を満たしているか確認しておく。

node --version
npm --version

この時点で次のように表示される場合は、まだ Node.js と npm が入っていない。

-bash: node: command not found
-bash: npm: command not found

Windows 側でも、次のように表示される場合は Node.js が入っていないか、PATH に通っていない。

'node' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。
'npm' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。

ローカルで記事を書いたり表示確認したりする作業は Windows 11 側で行い、本番公開に関わる Nginx、証明書、公開ディレクトリの作業は Raspberry Pi 5 側で行う想定。

Node.js をインストールする

Astro を使うには、ローカルの Windows 側にも、GitHub Actions の self-hosted runner が動く Raspberry Pi 5 側にも Node.js が必要になる。

Astro の現在の要件は Node.js v22.12.0 以上なので、LTS 版の Node.js 24 系を入れておくのが分かりやすい。

Windows 11 に入れる

Windows 11 では、winget が使えるなら次のコマンドで Node.js LTS を入れられる。

winget install --id OpenJS.NodeJS.LTS --exact

インストール後、開いているコマンドプロンプトや PowerShell をいったん閉じて、開き直す。PATH が更新されるまで、既存のターミナルでは nodenpm が見つからないことがある。

開き直したら確認する。

node --version
npm --version

PowerShell で npm --version を実行したとき、次のように npm.ps1 が読み込めないエラーになることがある。

このシステムではスクリプトの実行が無効になっているため、ファイル C:\Program Files\nodejs\npm.ps1 を読み込むことができません。
FullyQualifiedErrorId : UnauthorizedAccess

コマンドプロンプトでは npm --version が実行できるのに、PowerShell だけでこのエラーになる場合がある。これは Node.js のインストール失敗ではなく、PowerShell が npm.ps1 を読み込もうとして、実行ポリシーで .ps1 ファイルが止められている状態。

すぐ確認したいだけなら、コマンドプロンプトで npm --version を実行するか、PowerShell から npm.cmd を呼ぶ。

npm.cmd --version

PowerShell から通常の npm コマンドを使えるようにするなら、現在のユーザーだけ RemoteSigned にする。

Get-ExecutionPolicy -List
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
Get-ExecutionPolicy -List

Microsoft の説明では、PowerShell の実行ポリシーはスクリプトを実行する条件を制御する安全機能で、CurrentUser スコープは現在のユーザーだけに適用される。RemoteSigned はローカルで作成したスクリプトは署名なしで実行でき、インターネットから取得したスクリプトには署名を要求する設定。

設定後、PowerShell を開き直して確認する。

npm --version

この環境では、最初は CurrentUserUndefined だった。

CurrentUser       Undefined

Set-ExecutionPolicy の実行後は、CurrentUserRemoteSigned になった。

CurrentUser    RemoteSigned

その後、PowerShell でも通常の npm --version が通るようになった。

11.16.0

winget が使えない場合は、Node.js 公式サイトから LTS の Windows Installer をダウンロードしてインストールする。

https://nodejs.org/

Raspberry Pi 5 に入れる

Raspberry Pi 5 側では、GitHub Actions の self-hosted runner からも使いやすいように、システム全体に Node.js を入れる。

NodeSource の Debian/Ubuntu 向けリポジトリを使う場合は、次のようにする。

sudo apt update
sudo apt install -y curl ca-certificates
curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs

インストールできたら確認する。

node --version
npm --version

この環境では、Raspberry Pi 5 側で次のように表示された。

v24.18.0
11.16.0

Windows 11 側でも、同じように確認する。

node --version
npm --version

この環境では、Windows 側でも次のように表示された。

v24.18.0
11.16.0

v24.x.x のように表示されれば、Astro の要件は満たしている。

制限事項

この記事は、すでに nosubject.io を自分で管理していて、サーバ側の Nginx 設定も変更できる前提で書いている。

そのため、レンタルサーバや GitHub Pages、Cloudflare Pages、Netlify、Vercel などを使う場合は、DNS や HTTPS の設定画面が変わる。考え方は同じだが、Nginx の server ブロックや Docker Compose の設定はそのまま使えない。

また、DNS の反映時間、ルーターのポート開放、IPv4 と IPv6 の扱い、Let’s Encrypt の証明書発行方法は環境によって差が出る。この記事では、細かい管理画面の操作ではなく、サブドメインを新しい Astro サイトとして公開するために必要な設定のつながりを中心に整理する。

やりたいこと

今回の目的は次の通り。

  • 既存の nosubject.io はそのまま残す
  • create.nosubject.io を Roblox Studio 開発日記用のサイトにする
  • Astro の設定では site をサブドメインにする
  • Nginx ではメインサイトとは別の server ブロックで配信する
  • GitHub Actions などの自動ビルド先も別ディレクトリにする

サブドメインにしておくと、メインブログの記事一覧やカテゴリを混ぜずに済む。あとから独自ドメインへ移す場合も、サイト単位で移動しやすい。

サイト用のディレクトリを決める

まず、公開先のディレクトリをメインサイトと分ける。

たとえば Raspberry Pi 上で次のように分ける。

/home/<user>/nosubject-docker/hugo-public
/home/<user>/nosubject-docker/create-public

hugo-public は既存の nosubject.io 用、create-public は新しい create.nosubject.io 用にする。

Astro のビルド結果は、標準ではプロジェクト内の dist/ に出力される。GitHub Actions では、この dist/ の中身を /home/<user>/nosubject-docker/create-public へ同期する。

Astro サイトを作る

新しいリポジトリ、または新しい作業ディレクトリで Astro サイトを作る。

npm create astro@latest create-nosubject
cd create-nosubject

対話形式でテンプレートを聞かれたら、開発日記用には blog 系のテンプレートを選ぶと始めやすい。

今回の入力は次のようにした。

How would you like to start your new project?
  Use blog template

Install dependencies?
  Yes

Initialize a new git repository?
  Yes

Initialize a new git repository?Yes を選んだ場合は、Astro 側で git init まで実行される。完了すると次のように表示される。

Project initialized!
Template copied
Dependencies installed
Git initialized

最後に npm の新しいメジャーバージョンがあるという notice が出ることがあるが、Astro プロジェクトの作成自体は完了している。すぐに npm を更新しなくても、このまま次の作業へ進める。

最小構成で作った場合でも、あとから Markdown の記事一覧やタグページを足せる。ただ、制作日記として始めるなら、最初から blog template を選ぶ方が手早い。

Astro では、公開先のURLを astro.config.mjssite に設定する。

blog template で作成した直後の astro.config.mjs には、@astrojs/mdx@astrojs/sitemap、ローカルフォント設定が入っていた。これらは消さずに、sitehttps://create.nosubject.io になっていることを確認する。

// @ts-check

import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap';
import { defineConfig } from 'astro/config';

export default defineConfig({
  site: 'https://create.nosubject.io',
  integrations: [mdx(), sitemap()],
});

実際のファイルでは、さらに fontProviders.local() を使ったフォント設定も入っている。そこはテンプレートの見た目に関わる設定なので、そのまま残してよい。

import { defineConfig, fontProviders } from 'astro/config';

Astro の site は、sitemap や canonical URL などで使われる。サブディレクトリではなくサブドメインで公開するので、base は指定しない。integrationsfonts は別の役割なので、site を追加・確認するときに削除しない。

最初の記事を作る

blog テンプレートを選んだ場合は、src/content/blog/ の下に Markdown を追加する。

src/content/blog/hello-create.md

例として、次のような記事を置く。

---
title: '制作日記を始める'
description: 'Roblox Studio で試したことを残す場所を作る'
pubDate: 2026-07-18
tags: ['Roblox Studio', '開発日記']
---

Roblox Studio で試したことを、ここに残していく。

ローカルで確認する。

npm run dev -- --host 127.0.0.1 --port 4322

Astro の開発サーバーは標準で 4321 を使う。既存作業とぶつかる場合は、上のように別ポートを指定する。

本番用の静的ファイルを生成するには、次を実行する。

npm run build

ビルド結果は標準では dist/ に生成される。

この環境では、次のようにビルドできた。

[build] output: "static"
[build] directory: C:\Users\kkitta\GitHub\create-nosubject\dist\
├─ /blog/hello-create/index.html
├─ /rss.xml
├─ /index.html
[@astrojs/sitemap] `sitemap-index.xml` created at `dist`
[build] 9 page(s) built in 1.16s
[build] Complete!

/blog/hello-create/index.html が生成されているので、追加した記事もビルド対象に入っている。dist/ にできたファイル一式を、あとで Nginx の公開ディレクトリへ配置する。

DNS にサブドメインを追加する

nosubject.io はお名前.comで管理しているので、お名前.com Navi から create.nosubject.io のレコードを追加する。

お名前.comの公式ガイド「DNS関連機能の設定:DNSレコード設定」では、DNSレコード設定は次の流れで開く。

お名前.com Navi にログイン
  -> ネームサーバー/DNS
  -> ドメインDNS設定
  -> 対象ドメインの「ドメインDNS」
  -> DNSレコード設定
  -> レコード追加

対象ドメインは create.nosubject.io ではなく、親ドメインの nosubject.io を選ぶ。サブドメインのDNSレコードを追加する場合も、親ドメイン側のDNS設定に create というホスト名を追加する。

今回は、nosubject.io がすでに同じ Raspberry Pi 5 のサーバを向いている。さらに DNS 更新スクリプトで、外部から Raspberry Pi を参照できる IP アドレスが変わっても nosubject.io 側が更新されるようにしている。

そのため、create.nosubject.io にはサーバのIPアドレスを直接入れず、nosubject.io を参照する CNAME レコードを追加した。

ホスト名: create
TYPE: CNAME
TTL: 86400
VALUE(TARGET): nosubject.io

お名前.comの画面では、完全な create.nosubject.io ではなく、ホスト名に create だけを入力する。公式ガイドでも、xxx.domain.com の場合は xxx のみを入力する形で説明されている。

この形にしておくと、Raspberry Pi 側のIPアドレスが変わっても、DNS更新スクリプトが nosubject.io を更新すれば create.nosubject.io も同じ向き先を参照できる。

自宅サーバや VPS の IP アドレスへ直接向ける場合は、CNAME ではなく A レコードを使う。

ホスト名: create
TYPE: A
TTL: 86400
VALUE(TARGET): サーバのIPv4アドレス

IPv6 も直接指定する場合は AAAA レコードも追加する。

ホスト名: create
TYPE: AAAA
TTL: 86400
VALUE(TARGET): サーバのIPv6アドレス

入力したら、画面上の「追加」ボタンを押す。レコード一覧に追加されたことを確認してから、確認画面へ進んで設定を保存する。

途中でドメインプロテクションの申込み画面が表示された場合は、必要なければ「設定しない」を選んで進む。最後に確認画面で入力内容を確認し、「設定する」を押して完了させる。

お名前.comのDNSレコード設定では、TTL は 60 から 86400 秒の範囲で入力できる。特に理由がなければ、公式ガイドの入力例と同じ 86400 でよい。

反映を確認する。

dig create.nosubject.io

Windows の PowerShell なら次でも確認できる。

Resolve-DnsName create.nosubject.io

DNSの反映には時間がかかることがある。設定直後に引けなくても、レコード内容が正しければしばらく待ってから再確認する。

設定直後に Raspberry Pi 5 で確認したところ、次のように NXDOMAIN になった。

status: NXDOMAIN
ANSWER: 0

QUESTION SECTION:
create.nosubject.io.           IN      A

AUTHORITY SECTION:
nosubject.io.           300     IN      SOA     01.dnsv.jp. hostmaster.dnsv.jp.

NXDOMAIN は、create.nosubject.io という名前がまだDNS上に存在しないという意味。AUTHORITY SECTIONnosubject.io の SOA として dnsv.jp が出ているので、お名前.comの権威DNSまでは問い合わせできているが、create のレコードがまだ見えていない状態と読める。

この場合は、次の点を確認する。

  • お名前.comで親ドメイン nosubject.io を選んでいるか
  • ホスト名に create.nosubject.io ではなく create だけを入れているか
  • TYPE が CNAME になっているか
  • VALUE(TARGET) が nosubject.io になっているか
  • 「追加」だけで終わらず、最後の確認画面で「設定する」まで押したか
  • しばらく待ってから再度 dig create.nosubject.io を実行したか

DNSレコードが反映されると、ANSWER SECTIONcreate.nosubject.io の CNAME が表示されるようになる。

しばらく待ってから Windows 側で確認すると、次のように引けるようになった。

Name                           Type   TTL   Section    NameHost
----                           ----   ---   -------    --------
create.nosubject.io            CNAME  3600  Answer     nosubject.io

Name       : nosubject.io
QueryType  : A
TTL        : 3600
Section    : Answer
IP4Address : 60.84.210.40

これで create.nosubject.ionosubject.io を参照し、その先で現在のIPv4アドレスへ解決できていることが確認できた。

Nginx にサブドメイン用の設定を追加する

Nginx では、既存の nosubject.io 用とは別に create.nosubject.io 用の server ブロックを作る。

server {
    listen 80;
    listen [::]:80;

    server_name create.nosubject.io;

    root /var/www/create;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html =404;
    }

    error_page 404 /404.html;

    location = /404.html {
        internal;
    }
}

Astro の静的出力では、ページごとに index.html が生成される構成が多い。try_files $uri $uri/ /index.html =404; としておくと、通常の静的ページと、クライアント側でルーティングするページの両方を扱いやすい。

Docker Compose で Nginx を動かしている場合は、ホスト側の create-public をコンテナ内の /var/www/create へマウントする。

services:
  web:
    image: nginx:stable
    volumes:
      - ./hugo-public:/var/www/html:ro
      - ./create-public:/var/www/create:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro

メインサイトとサブドメインで root を分けるのが大事。ここを同じにすると、どちらのドメインでも同じサイトが表示される。

設定を書いたら構文を確認する。

docker compose exec web nginx -t

問題なければ Nginx を reload する。

docker compose exec web nginx -s reload

HTTPS 証明書を用意する

サブドメインは、メインドメインとは別名として証明書に含める必要がある。

Certbot を Nginx 連携で使っているなら、次のように create.nosubject.io を追加する。

sudo certbot --nginx -d create.nosubject.io

既存の証明書に nosubject.iowww.nosubject.io をまとめている場合でも、create.nosubject.io が証明書に入っていなければ HTTPS では使えない。

証明書を発行したら、ブラウザで次を開く。

https://create.nosubject.io/

まだ Astro の dist/ を配置していない場合は 404 や空の表示になることがある。Nginx と証明書の段階では、まず HTTPS の警告が出ないところまで確認できればよい。

GitHub Actions のビルド先を分ける

自動ビルドする場合は、Astro の dist/ をメインサイトとは別の公開先へ同期する。

たとえば新しい Astro サイトのリポジトリに、次のような workflow を置く。

name: Build Create Astro site

on:
  push:
    branches:
      - main
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build:
    runs-on: [self-hosted, Linux, ARM64, rpi5]
    env:
      ASTRO_DESTINATION: /home/<user>/nosubject-docker/create-public
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Publish
        run: |
          test -d "$ASTRO_DESTINATION"
          rsync -a --delete dist/ "$ASTRO_DESTINATION"/

ポイントは、dist/ の中身を create-public へ同期すること。

ASTRO_DESTINATION: /home/<user>/nosubject-docker/create-public

ここを既存サイトと同じ /home/<user>/nosubject-docker/hugo-public にしてしまうと、メインサイトの出力を上書きしてしまう。

公開後に確認する

ビルドと Nginx の設定が終わったら、次の順番で確認する。

curl -I https://create.nosubject.io/

HTTP/2 200HTTP/1.1 200 OK が返ればトップページは表示できている。

存在しない URL も確認する。

curl -I https://create.nosubject.io/not-found-test/

404 ページを用意している場合は、ステータスコードが 404 のまま、本文として Astro の 404.astro から生成されたページが表示される状態にする。

最後に、Astro 側の URL も確認する。

  • トップページが https://create.nosubject.io/ で開ける
  • 記事ページの URL が https://create.nosubject.io/.../ になる
  • sitemap や canonical URL が nosubject.io ではなく create.nosubject.io になる
  • メインサイト https://nosubject.io/ は今まで通り表示される

Search Console などに登録する

サブドメインは、検索エンジンや解析ツールから見ると別サイトとして扱うことがある。

必要であれば、次のサービスにも create.nosubject.io を追加する。

  • Google Search Console
  • Google Analytics
  • AdSense
  • Bing Webmaster Tools

最初から全部を設定しなくても、記事を増やしていくなら Search Console だけは早めに登録しておくと、インデックス状況を確認しやすい。

まとめ

create.nosubject.io のようなサブドメインで Astro サイトを分ける場合、作業は次の順番で進めると分かりやすい。

  1. 新しい Astro サイトを作る
  2. astro.config.mjssitehttps://create.nosubject.io にする
  3. お名前.comで create のDNSレコードを追加する
  4. Nginx に create.nosubject.io 用の server ブロックを追加する
  5. HTTPS 証明書を発行する
  6. GitHub Actions で npm run build し、dist/ を専用ディレクトリへ同期する
  7. curl とブラウザで表示を確認する

新しいドメインを取るより、サブドメインの方が始めるまでの摩擦が少ない。Roblox Studio の開発日記として続けてみて、記事が増えてから独自ドメイン化する、という流れでもよさそうだ。