AstroとWordPressの使い分け ― 初Astro案件で画像404を踏んだ話
地域イベントの LP 案件で、Astro でビルドした成果物をエックスサーバーに上げた瞬間、全ての画像が 404 になりました。
テスト環境では問題なく動いていたのに、本番環境でだけ起きる現象です。
納期は 3 日後、動かしようがない状況で、応急処置と根本解決を分けて乗り切りました。
この記事では、自分が Astro と WordPress をどう使い分けているか、そしてその案件で踏んだ tsx 経由のビルドパス問題をどう解決したかを整理します。
なぜ WordPress ではなく Astro を選んだか

この案件は、親しい関係のクライアントから「全部お任せします」と言われた地域イベントの LP でした。
納品物は 1 ページ構成で、トップカルーセル、カウントダウン表示、スクロール連動のアニメーション、問い合わせフォーム、Google Maps 埋め込み、YouTube 埋め込み、SNS 埋め込み、GIF 動画と、見た目には盛り沢山です。
このサイトには、次の 3 つが 1 つも必要ありませんでした。
- クライアントによるコンテンツ更新 (公開後は基本的に触らない)
- 管理画面ログイン機能
- クライアント自身がレイアウトを変える可能性
WordPress を選ぶ理由が 1 つも無かった、というのが正直なところです。
逆に WordPress を選ぶと、PHP の実行環境とデータベース、プラグインの定期アップデート、セキュリティパッチ対応、そして管理画面からのアカウント侵入リスクが全部ついてきます。
「更新しないサイト」に対してこれらを背負わせる合理性が見つかりません。
Astro を選んだもう 1 つの理由は、Vite と Astro の組み合わせによる開発体験です。
HMR の速さ、TypeScript サポート、そして Islands Architecture で必要な部分だけ部分的に React コンポーネントを挿し込める柔軟性は、スピード勝負のクライアント案件と相性が良いと感じていました。
クライアントには「Astro」という単語は一度も伝えていません。
必要なのは動く完成品だけで、その裏側の技術選定は自分に任されていた関係性だったからです。
本番デプロイ後、画像が全て 404 になった
LP のトップにカルーセル表示を入れたく、Swiper を使うことにしました。
Swiper は .astro ファイルに直接書くより、React または Vue のラッパー経由で使う方が API が素直です。
自分は Astro の @astrojs/react インテグレーションを入れて、カルーセルのセクションだけ .tsx で書きました。
他のセクションは全部 .astro で書き、tsx で書いたのはこの 1 コンポーネントだけ。
この判断自体は後悔していませんが、ここに後から響く落とし穴がありました。
ローカルでは npm run build して npm run preview で動作確認済みでした。
エックスサーバーに FTP で成果物をアップロードして、クライアントに確認用 URL を送ろうとアクセスした瞬間です。
画像が 1 枚も表示されず、Swiper も動かない状態でした。
DevTools のネットワークタブを開いて原因は即座に分かりました。
ブラウザが画像や JavaScript を取りに行っているパスが、全てドメイン直下からの絶対パスになっていました。
具体的には、配信先が https://example.com/events/summer/ のような 2 階層のサブディレクトリだったのに、ブラウザは https://example.com/_astro/xxx.js のような、ルートドメイン直下を取りに行っていたのです。
そこにはファイルが無いので当然 404 です。
Astro は設定を何も書かないと、ビルド時に /_astro/... や /images/... のようなドメイン直下起点の絶対パスで HTML を出力します。
ローカルの preview モードはルートディレクトリから配信するので問題が表面化しませんが、サブディレクトリに置いた瞬間、全てのリンクが壊れます。
応急処置 ― astro-relative-links で「まず動かす」

納期は 3 日後、クライアントに確認してもらう時間も必要です。
選んだのは応急処置を先に打つという方針でした。
根本解決には少し調査時間がかかりそうだったので、まず動く状態を作ることを優先しました。
探して見つけたのが astro-relative-links という Astro インテグレーションです。
ビルド時に HTML 内の絶対パスを全て相対パスに変換してくれる仕組みで、サブディレクトリ配置・オフライン配布・ポータブルビルドのどれにも効きます。
インストールと設定は 1 行コマンドと数行の追記で完了します。
npx astro add astro-relative-links// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import relativeLinks from 'astro-relative-links';
export default defineConfig({
integrations: [react(), relativeLinks()],
});再ビルドすると HTML の <link href=""/_astro/xxx.css"> が <link href=""./_astro/xxx.css"> のような相対パスに変換されて出力されます。
エックスサーバーのサブディレクトリ配置でも全ファイルが正しく読み込まれて、画像も Swiper も動き始めました。
これで公開日の 3 日前の夜を乗り切れました。
根本解決 ― astro.config.mjs の site / base / assetsPrefix
公開後、時間ができた時に正規の解決策を調べました。
Astro の公式ドキュメントに答えがあり、サブディレクトリ配置するときは site と base を設定するのが本筋です。
実際に追加した設定がこちらです。
// astro.config.mjs (after)
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
site: 'https://example.com',
base: '/events/summer/', // 2 階層のサブディレクトリに合わせる
build: {
assetsPrefix: '/events/summer/',
},
integrations: [react()],
});site は公開 URL のオリジン、base はデプロイ先のサブディレクトリパスです。
さらに build.assetsPrefix を同じ値で指定することで、ビルド後の _astro/ 配下のアセット参照も正しいプレフィックスで出力されます。
この設定にすると、HTML の画像パスが /events/summer/_astro/xxx.js のようにサブディレクトリを含んだ絶対パスで出力されるようになり、エックスサーバーの配置と一致します。
応急処置で入れていた astro-relative-links は外して、正規の設定だけで動く状態にしました。
次の Astro 案件からは、最初にこの 3 つを設定してから書き始めると決めています。
サブディレクトリ配置の可能性がある案件なら、tsx コンポーネントを使う使わないに関係なく、最初の 5 分で済ませておくべき下準備です。
この案件から決めた自分の使い分けルール
今回の経験を経て、自分の中で次のルールに落ち着きました。
以下の 3 つが 1 つも該当しないサイトは、Astro で作ります。
- クライアントが自分でコンテンツを更新する (公開後に触る運用)
- 管理画面からの操作が必要 (ログイン機能・会員機能)
- クライアントが意図せずレイアウトや文言を変える可能性がある
逆に 1 つでも該当したら、WordPress かヘッドレス CMS と静的ジェネレータの組み合わせを選びます。
動き・アニメ・Swiper・フォーム・外部埋め込みなど、見た目側の要件は Astro でもほぼフルに実装できます。
カウントダウンや複雑なインタラクションも自作 React コンポーネントでまかなえるので、「動きが多いから WordPress」は理由になりません。
Astro のおかげで解放された一番大きなことは、公開後の保守疲れです。
WordPress で運用していると、コアやプラグインやテーマの定期アップデート、管理画面ログインの監視、クライアントが気づかないうちに変更してレイアウトが崩れる事故対応、こういった工数が毎月のように発生します。
Astro で納品した静的 LP には、そもそもこれらが存在しません。
静的ファイルが上がっているだけなので、触らなければ何も壊れない状態を長期間維持できます。
Astro と WordPress の使い分けをシンプルに言語化するなら、「誰がどう更新するか」を起点に決めるのが一番ズレないです。
更新する人がクライアントなら WordPress、更新する人がいない・あるいは自分だけなら Astro。
それで 8 割方の判断は済みます。
ただし、tsx コンポーネントを使う場合は今回のビルドパス問題を踏む可能性があるので、サブディレクトリ配置の案件では最初から site / base / assetsPrefix を設定しておくことを忘れないようにしています。
まとめ
- WordPress は CMS、つまり「クライアントが自分で更新する」ための道具です
- クライアントが更新しない・管理画面が要らない・意図しない変更リスクがない、の 3 条件が揃ったら Astro を選ぶ
- tsx コンポーネントを使った Astro サイトをサブディレクトリに配置すると、ビルド後のアセットパスがドメイン直下絶対パスになって 404 を起こすことがある
- 時間がない時は
astro-relative-linksインテグレーションで応急処置できる - 根本解決は
astro.config.mjsのsite/base/build.assetsPrefixを設定すること - 静的サイトで納品することで、保守フェーズのトラブルが構造的に発生しない状態を作れる
この案件は「Astro という単語を知らないクライアント」への納品でしたが、完成品のクオリティとその後の保守の軽さで、今も信頼関係を保てています。
次の案件で技術選定に迷ったら、「このサイトは誰がどう更新するか」を最初に確認してみてください。
そこから逆算するだけで、WordPress が必要なのか、Astro で十分なのかが見えてきます。
Astro 案件の実装や、WordPress からの移行について相談があれば、お気軽にご連絡ください。
案件の要件に合わせた技術選定から実装・保守まで対応しています。