PostHog を入れて、本番だけで計測するようにするまで
2026-08-28
登録したツールを1つずつ使っていく1本目。個人サイトに PostHog を入れた。難しかったのは導入そのものではなく、プレビュー環境とローカルから送信されないようにすることだった。
PostHog wizard導入
ディレクトリを間違えても壊さなかった
別のリポジトリで実行してしまったが、wizard は「ここに Next.js アプリはない」と判定して何もしなかった。ただしキャッシュと .claude/skills/ は置いていった。このとき .claude/ が gitignore されていないことに気づいた。エージェントが生成するファイルが増えているので、無視設定を先に入れておくほうがいい。
PostHog環境の出し分け
wizard のガードは「トークンがあるか」しか見ていない
それだと環境変数を継承する仕組みの下では漏れる。Railway の PR 環境は production の変数を引き継ぐので、プレビューからも本番の PostHog に送られる。ローカルも .env.local にキーがあれば同じ。判定を window.location.hostname === 本番ホスト に変え、capture を全部その関数経由にした。キーが入っていても本番以外からは送られない形にできる。
Railway環境変数
NEXT_PUBLIC_ を後から設定してもバンドルは更新されない
ビルド時に埋め込まれる値なのに、変数変更で走る再デプロイは既存イメージを使い回した。デプロイは SUCCESS なのに、配信されている JS には値が入っていない。railway redeploy --from-source で最新コミットを取り直させると入る。「デプロイ成功」と「配信物が新しい」は別。
ブラウザの開発ツールネットワーク監視
クロスオリジンの送信が見えず、検証を1度間違えた
使っていたネットワーク一覧にはクロスオリジンのリクエストが出てこなかった。そのため「送信されていない」と判断したが、実際には見えていないだけだった。本番で送信が成功しているのに0件と表示されたことで気づいた。performance.getEntriesByType("resource") は取りこぼさない。送信の有無だけでなく、初期化されたか(cookie と localStorage のキー)も一緒に見ると、どちらの理由でゼロなのか区別できる。
公開前に確認できるようにするまで(Railway の PR Environments)
2026-08-28
main への push がそのまま公開サイトに反映される状態だった。レビューなしで外に出続けていたので、PR を経由する形に変えた。
RailwayPR Environments
有効化する前に作った PR には環境が立たない
設定を入れても、既存の PR には webhook が飛んでいないので何も起きない。PR を一度クローズして再オープンすると作られる。以後の PR は15秒ほどで自動的に立った。マージまたはクローズで自動削除される。
RailwayBase Environment
未設定だと PR 環境が本番の変数を継承する
設定画面に「Base Environment が未定義なら、宛先ブランチに紐づく環境が使われる」と書いてある。宛先が main なら production。つまり本番のキーがプレビューにも入る。preview 環境を作って指定すれば分離できるが、常設環境が増えてクレジットを食う。かつ実質は「preview 側にキーを入れない」運用への依存なので、うっかり入れたら漏れる。コード側で判定するほうが安上がりで、設定ミスにも強かった。
Railway で個人サイトを公開して独自ドメインに切り替えるまで
2026-08-10
Next.js の1ページだけの個人サイトを Railway に載せ、既に持っていた gotch.work に切り替えた。ドメインの DNS は Vercel のネームサーバー上にあり、同じドメインで古いサイトが動いていた。
RailwayGitHub 連携デプロイ
private リポジトリのままデプロイできる
public にする必要はなかった。Railway の GitHub App に対象リポジトリへのアクセスを明示的に許可するだけ。プランによる制限もない。迷ったら private でよく、公開はあとからでもできるが逆は取り返しがつかない。
Railwayサービスドメイン
デプロイが成功しても、外からは開けない
公開ドメインは自動で生えない。Settings → Networking → Generate Domain(CLI なら railway domain)を実行するまで、ビルドもデプロイも success なのに URL が存在しない状態になる。「デプロイ成功」と「公開」は別。
RailwayCLI / link
対話プロンプトを踏まずにプロジェクトへ紐付けられる
railway link -p <projectId> -e <environmentId> -s <serviceId> で非対話に指定できる。ID は GitHub の deployments API から拾えた。gh api repos/<owner>/<repo>/commits/main/status の target_url に project / service / environment の ID が入っている。
Railwayカスタムドメイン
1サービスにカスタムドメインは1つまで(プラン制限)
apex を追加したあと www を足そうとして拒否された。エラーは You have reached the limit for custom domains per service on your plan。www のためにプランを上げる理由が薄かったので www は使わないことにした。
Vercel DNSALIAS レコード
apex には CNAME が張れないので ALIAS を使う
Railway が要求するのは CNAME @ → xxxx.up.railway.app だが、DNS の仕様上 apex に CNAME は置けない。Vercel DNS の ALIAS で代替できた。vercel dns add <domain> @ ALIAS <target>。所有確認用の TXT も同じ方法で追加する。
Vercelプロジェクトとドメインの紐付け
DNS を向けても切り替わらなかった。いちばん詰まった点
ALIAS を Railway に向けたのに、ブラウザからは古いサイトが出続けた。原因は、そのドメインが Vercel の古いプロジェクトに紐づいたままで Vercel 側が優先して応答していたこと。curl -I のヘッダに server: Vercel と出ていたのが決定的な手がかりだった。古いプロジェクトを削除したら解決した。DNS を疑う前に「誰が応答しているか」をヘッダで見るのが速い。
Vercel DNSCAA レコード
CAA が別 CA を許可していたおかげで証明書は素通りした
Vercel が既定で letsencrypt.org を含む CAA を置いていた。Railway は Let's Encrypt を使うので、追加作業なしで証明書が発行された。もし CAA が特定 CA だけを許可していたら、ここで詰まっていた。
RailwayIaC / CLI
Terraform を入れる意味がなかった
選択肢は3つ。Railway CLI(安定・リポジトリに何も増えない)、Railway 公式 IaC(experimental・TypeScript SDK が依存に増える)、Terraform provider(非公式の community 製・infra ディレクトリと state 管理が増える)。決め手は、いちばん面倒な GitHub App の認可がブラウザの OAuth なのでどの方法でも自動化できない点。一番の手間が手動で残るなら、IaC を入れる主な理由が消える。コマンドを README に書き残す形にした。