どぅも、こんちくわー
タイトルにあるように、HonoでSSR(Server Side Rendering)を配信していたのですが、
ReactとHonoで分けたほうがイマっぽくね〜🕶
と思って分けたら、結構しっかりハマったのでその反省文 ナレッジを記載したいと思います!
主にこれらが重要なパス指定なので、非常に大事になります
- Caddy
- Vite base
- React Router basename
- fetch先URL
- deploy先
この流れで配信する想定です。
bun run build
↓
dist 生成
↓
Caddy が静的配信
ハマりポイント1
Vite/Reactのローカル開発手法と、静的ファイル配信はパスが若干変わるので、そこがハマりましたね。
開発: vite dev server
本番: build した dist を静的配信
ハマりポイント2
Caddyfile の root パスが間違っていた。そのため、distフォルダの指定が誤っていたので、not foundになる。
Caddy が存在しないパスを見ていたので、当然配信できない。
解決
Caddyfile の root を実際のサーバーパスに合わせた。
しっかりとlsを使って確認しよう涙 あたりまえですがねぇ
ハマりポイント3
/home/ubuntu の権限で 403 になった
/home/user/hono-repoo/distを参照していたら、rootからは実行不可により、403 Forbidden(権限なし)となってしまっていた。
namei -l で確認したら下記の状態で、caddyユーザーの権限がなく、403 errorであった。
/home/user -> drwxr-x---
最終的にはリポジトリ直下をそのまま配信するのではなく、公開用ディレクトリへコピーする方針に寄せた。
/var/www/my-domain/react-app/frontend
ハマりポイント4
vite.config.ts の base が / で壊れていた
App.tsx はこうなっていた。
<BrowserRouter basename="/hono-note/frontend">
でも vite.config.ts はこうでした。
base: "/"
React Router の URL は /react-app/frontend 前提なのに、
JS/CSS の asset パスは /assets/... 前提で build される可能性がある。
解決
本番の base を /react-app/frontend/ に合わせた。
学び
basename と base は別物だけど、公開パスがあるなら両方の整合が必要。
React Router basename -> 画面ルーティング
Vite base -> JS/CSS/画像の配信パス
ハマりポイント5
dist/index.html は正しいのに JS/CSS が 404 だった
状況
dist/index.html を見ると、読み込む JS/CSS は正しかった。
<script src="/hono-note/frontend/assets/index-....js">
<link href="/hono-note/frontend/assets/index-....css">
しかし、ブラウザではこれが 404 だった。
/hono-note/frontend/assets/index-....js
/hono-note/frontend/assets/index-....css
学び
HTML が返せていても、asset が返せていなければ画面は壊れる。
まず curl -I で asset を直接叩くのが大事。
ハマりポイント6
handle_path / try_files の理解不足で混乱した
状況
Caddy 側で handle_path と try_files を使っていた。
問題
try_files {path} /index.html は SPA 用には便利だが、
存在しない JS/CSS に対しても index.html を返してしまうと、ブラウザ側では
module script が欲しいのに HTML が来た
になってエラーになる。
実際にこうなった。
Failed to load module script
解決
curl -I で実体を確認し、
「JS/CSS に 200 が返っているか」「Content-Type が正しいか」を見た。
学び
SPA の fallback は強力だけど、
asset の 404 を隠してしまうので切り分けが難しくなる。
curl -I は必須。
ハマりポイント7
ファイル名の見間違いでも混乱した
状況
途中で確認した URL が実際の build 生成物とズレていた。
例:
index-DkDJhGV1.js が本物
なのに
index-Cim0XEfe.js を見ていた
あるいは逆に
.js と .css を取り違えて curl していた
問題
存在しないファイルを見て「設定がおかしい」と思い込みやすい。
解決
dist/index.html に書かれている実ファイル名をそのまま使って curl -I した。
学び
ハッシュ付き asset は毎回変わる。
必ず dist/index.html を source of truth にする。
ハマりポイント8
deploy が「build だけ」で、公開先反映が抜けていた
状況
最初の prd-deploy.sh はこれだけだった。
bun install
bun run build
sudo systemctl reload caddy
問題
build はされるが、Caddy が見ている公開ディレクトリに新しいファイルが反映されない。
その結果、
HTMLは新しい
assetsは古い
みたいなズレが起こる。
解決
dist を公開ディレクトリへ同期する工程が必要だと分かった。
rsync -av --delete dist/ /var/www/my-domain/react-app/frontend/
学び
本番 deploy は build だけでは終わらない。
解決方法まとめ
- BrowserRouter basename と Vite base は揃える。
- 本番の Vite は dist を配信する。5173 は開発用。
- Caddy が見るディレクトリと deploy 先は一致させる。
- /home/... 配下を直接配信すると権限でハマりやすい。
- curl -I で asset を直接確認すると、かなり早く切り分けできる。
- dist/index.html に書かれた実ファイル名を信じる。
- rsync --delete は hashed asset 配信でかなり重要。
最終的に理解すべき構成
これが今回の正解です。
React/Vite source
↓
bun run build
↓
dist
↓
rsync で公開ディレクトリへ反映
↓
Caddy が静的配信
API 側は別。
Browser
↓
Caddy
↓
/hono-note/backend/* -> Hono
/hono-note/frontend/* -> 静的ファイル
まとめ
個人開発をしているといろんな学びがありますねぇ〜
なるべく、本番とローカルで環境に差異がないように努めるのも大事ですね!
お茶目ミスも多かったですが、これが同時にハマるとまじで見えないんですよね。。
このようなミスがないと経験値がアップしないので、やってみてよかったです!
では!