[{"data":1,"prerenderedAt":304},["ShallowReactive",2],{"content-/app-cutover-udemy-links-finish":3,"all-pages-for-dir":284,"related-/app-cutover-udemy-links-finish":285,"og-image-/app-cutover-udemy-links-finish":303},{"id":4,"title":5,"body":6,"category":266,"concepts":267,"description":268,"extension":269,"meta":270,"navigation":220,"ogImage":267,"path":271,"project_name":272,"published":273,"publishedAt":274,"seo":275,"stem":276,"tags":277,"todo":267,"unpublished":273,"updatedAt":267,"__hash__":283},"pages/2026-07/2026-07-28/app-cutover-udemy-links-finish.md","Herokuを止める前に導線を移す — app.eurekapu.com 静的化の本番切替とUdemyリンク62本の移設",{"type":7,"value":8,"toc":250},"minimark",[9,13,16,20,23,26,29,32,35,38,41,44,47,50,53,56,59,63,66,69,72,75,78,81,88,91,94,97,101,104,107,115,118,122,125,165,168,175,179,182,185,188,191,194,197,200,203,206,209,235,238,241,247],[10,11,12],"p",{},"朝の時点で、Googleタスクに今日期限のものが4件並んでいた。本命は app.eurekapu.com の静的化の本番切替で、これはDNSを1レコード書き換えれば終わる。手順は前日に確定している。",[10,14,15],{},"ただ、順番が引っかかっていた。",[17,18,19],"h2",{"id":19},"まず2件を閉じた",[10,21,22],{},"1Password のサービスアカウント化は、調べさせたら未実施だった。記憶では終わっているつもりでいたので逆の結果だったが、急ぐ理由もないし、そもそもやる必要がないと判断してタスクごと閉じた。将来のセッションが計画書を見て再着手しないよう、計画書の冒頭に中止の一行だけ足させた（既存の内容は消さずに残す）。",[10,24,25],{},"教材化のほうは、PR 12本がすべてマージ済みで、12講が公開状態になっていることを証拠つきで確認できた。こちらも閉じる。",[10,27,28],{},"残ったのが、Udemyのスライドリンクの移設と、app静的化の本番切替。この2つだった。",[17,30,31],{"id":31},"どちらを先にやるか",[10,33,34],{},"Udemyの自講座（会計入門系）の補足リンクは、app.eurekapu.com のページを指している。そのappを今日Cloudflareへ移し、数日後にはHerokuを止める。",[10,36,37],{},"先に静的化を終わらせてからリンクを移すのか、逆か。聞いてみたら、スライドからで正しいという答えが返ってきた。理由は単純で、Udemyの導線を先に info-accounting.com へ移してしまえば、あとでDNSを切ってHerokuを止めるとき、受講生への影響が構造的にゼロになる。切替時に何が起きても怖くない状態を先に作る、という順序だ。",[17,39,40],{"id":40},"移設先はもう出来上がっていた",[10,42,43],{},"調査させたら、思っていたより話が小さかった。",[10,45,46],{},"info側にはスライドビューアとデータがすでに移植済みで、公開もされていた。画像1,286枚も別ドメインのR2に移してあった。残っていたのは、Udemyが参照している61ページを info側のURLへ対応づけて、リンクを差し替える作業だけだった。",[10,48,49],{},"対応表は機械抽出させた。62リンクのうち、直接ひけるのが24本、スライドのグループから導出するのが38本、未解決ゼロ。",[17,51,52],{"id":52},"読み上げ原稿は自分だけ見えればいい",[10,54,55],{},"スライドビューアには、読み上げ原稿のセクションとURLのセクションがある。これは受講生に見せるものではないので、adminアカウントだけに表示を絞ってもらった。",[10,57,58],{},"devで両方の状態を確認したところまでは報告が上がってきたが、これは本番で自分の目で見るまで信じない類のものだ。デプロイまでやってもらい、非adminのアクセスで両セクションが消えることを本番で確認させた。",[17,60,62],{"id":61},"差し替えは通った導線は通らなかった","差し替えは通った。導線は通らなかった",[10,64,65],{},"Udemyには一括更新のUIがない。ログイン済みのChromeから、リソースを1件追加するときのリクエストを実際にキャプチャさせて、asset作成のAPIルートを特定した。あとは13件ずつバッチで叩くだけになる。",[10,67,68],{},"バッチ1が13/13。バッチ2も13/13。",[10,70,71],{},"そこで受講生プレビューを開いて、リソースを実クリックさせた。ここで手が止まった。演習系のリンクは、info側に受け皿がない。スライドは移してあったが、演習ページは app に残ったままだった。",[10,73,74],{},"演習系38本は一旦appに戻すほかない。差し戻しは、元タイトルと旧URLで作り直してから新しいリンクを消す逆向きの手順で、38/38成功。全数検証で元の状態と一致した。",[10,76,77],{},"戻したことで、問題の形ははっきりした。演習ページがそもそもappから移行できていない、というだけの話だ。移植の計画書を作らせて、別セッションへ渡した。",[17,79,80],{"id":80},"静的化の本番切替",[10,82,83,87],{},[84,85,86],"code",{},"nuxt generate"," の再実行から。約12分でEXIT=0、HTML 795・4,610ファイル。ログに出た \"error\" 13件は本物かどうか中身を確認させた。",[10,89,90],{},"Cloudflare Pagesに新規プロジェクトを作って、4,610ファイルのデプロイが63秒。プレビューでスライドの描画、仕訳クイズのクリック、主要ページの200を確認してから、カスタムドメインを追加した。DNSレコードはHerokuのホスト名からPages側へ自動で書き換わる。",[10,92,93],{},"切替は即時だった。切替直後のプローブでPages配信を確認して（トレイリングスラッシュの308が識別子になる）、全794ルートのHTTPスイープが794/794の200。",[10,95,96],{},"エラーが出たらすぐ戻せるように、ドキュメントの一番上にロールバック手順を書かせた。戻すべきDNSの値、直リンク、Pages側のカスタムドメインを先に外す注意。Herokuは止めずに稼働させたままにしてある。",[17,98,100],{"id":99},"codex-2並列でui全面テスト","Codex 2並列でUI全面テスト",[10,102,103],{},"794ページを367と427に割って、Codexを2本並列で走らせた。",[10,105,106],{},"配信面はクリーンだった。794/794が200、参照アセット835も全部200、致命的な配信障害ゼロ。",[10,108,109,110,114],{},"問題はそこに映らないところにあった。",[111,112,113],"strong",{},"200を返すのに本文が空のページが2件","。HTTP監視では永久に見つからない類のもので、片方はExcelセミナーの1ページ、もう片方は演習の空スタブだった。",[10,116,117],{},"品質面の宿題も出てきた。description欠落204ページ、h1欠落60ページ、alt欠落154画像、汎用titleが48ページ。",[17,119,121],{"id":120},"_40分で直せる分だけ直した","40分で直せる分だけ直した",[10,123,124],{},"すぐ終わらないのかと聞いたら、すぐ直せる範囲と重い範囲を分けて出してきた。すぐ直す方を選んだ。",[126,127,128,143,149,155],"ul",{},[129,130,131,134,135,138,139,142],"li",{},[111,132,133],{},"alt欠落154画像","の大半は、共通コンポーネントの1文字のタイポだった。",[84,136,137],{},":alt=\"item.imgAlt\""," と書いてあり、レイアウト側は元から ",[84,140,141],{},"imageAlt"," を渡していた。1行直すだけで大量ページのaltが復活した",[129,144,145,148],{},[111,146,147],{},"description欠落204ページ"," — 設定ファイルの既定descriptionを日本語の実文に変えて、全ページのフォールバックとして効かせた",[129,150,151,154],{},[111,152,153],{},"空スタブ1件"," — ページを削除して301",[129,156,157,160,161,164],{},[111,158,159],{},"もともと500だった3ルート"," — 存在しない章キーでフィルタして0枚になりクラッシュしていた既存バグ。",[84,162,163],{},"_redirects"," を新設して301で実在ページへ逃がした",[10,166,167],{},"再生成して再デプロイ、本番で301とaltとdescriptionを実測確認。ここまで約40分だった。",[10,169,170,171,174],{},"残った1ページは直せていない。generateのときだけSSRが静かに失敗して、SPAシェルだけが出力される。devサーバーでは成功する。2ビルド連続で、794ページ中この1ページだけが同じように落ちた。ブラウザではJSが起動すれば正常に表示されるので、実害はSEOと初期描画に限られる。空リンクの ",[84,172,173],{},"\u003Cnuxt-link to=\"\">"," を疑って除去したが、原因ではなかった。",[17,176,178],{"id":177},"午後演習系38リンクをinfoへ","午後、演習系38リンクをinfoへ",[10,180,181],{},"朝渡した計画書を別セッションで実行した。移植元と移植先の理解を5体の並列調査に分担させてから着手している。",[10,183,184],{},"決裁フォームの項目を読んでいて思い出したことがあった。appの演習ページは、ログインなしで見える形で公開していた。無料で開けるようにしていたのを、すっかり忘れていた。",[10,186,187],{},"だとすれば移植先も同じ扱いでいい。認証は設けず、サイト内の目次・一覧・内部リンクには載せない公開ページを作る。実質、Udemyのリソースリンクを知っている人だけが来るページになる。",[10,189,190],{},"37ページ・108設問を生成した。画像は、設問側566枚がすでにR2へ複製済みで、足りなかったのは○×バーの3枚だけ。ただしスライド画像は抜けていて、あとから141枚を追加でアップロードした。",[10,192,193],{},"描画は自動で通ったことにせず、タイプごとに実クリックで確かめさせた。クイズの選択から答え合わせ、解答解説の展開、スライドを送ると財務諸表の数字が動く連動（普通預金 1000→700、売上原価 300 計上）まで。37ページ全部の描画を確認してからデプロイした。",[10,195,196],{},"Udemy側の差し替えは3バッチ（13/13/12）で38件全成功。全数再検証で、演習系のappURL残存はゼロになった。これで自講座のスライド系62リンクは全部infoに移った。",[17,198,199],{"id":199},"別リポジトリの積み残しも2件",[10,201,202],{},"eurekapu-nuxt4 は、pushとデプロイ（6.5分）と履歴CSVの追記。前日から持ち越したWebKitのE2E 4件フェイルは、webkit固有のバグではなく、devサーバーのコールドスタート由来のフレーキーだと確定した。ウォーム状態では再現せず、ワーカーの終了が5分ハングして強制killされているログが残っていた。前日「単独再実行が0バイトのまま終わった」ように見えたのも、たぶん同じ現象だった。",[10,204,205],{},"wordpress-migration のほうは、前日の画像障害の復旧が本番で生きていることを実測で確認した。恒久対応はこちらではなく mdx-playground 側でやると決めて、進捗台帳に一言足してコミットした。",[17,207,208],{"id":208},"残っていること",[126,210,213,223,229],{"className":211},[212],"contains-task-list",[129,214,217,222],{"className":215},[216],"task-list-item",[218,219],"input",{"disabled":220,"type":221},true,"checkbox"," Herokuの停止 — ダッシュボードのThroughputが数日 0 rps を続けたら止める。不可逆なので自分の手でやる",[129,224,226,228],{"className":225},[216],[218,227],{"disabled":220,"type":221}," SSRが1ページだけ失敗する件 — 生成の並列度を絞って再生成するか、移植のときに吸収するか",[129,230,232,234],{"className":231},[216],[218,233],{"checked":220,"disabled":220,"type":221}," Google側の対応 — ドメイン・URL・コンテンツ・HTTPSが全部不変でホスティングだけが変わったので、Search Consoleの再登録もタグの入れ直しも不要と確認済み",[17,236,237],{"id":237},"学んだこと",[10,239,240],{},"順番の判断は結果的に正しかった。逆順でDNSを切ってから「演習系の受け皿がない」と気づいていたら、受講生のリンクが切れた状態で慌てて直すことになっていた。移設は導線が先、インフラが後。",[10,242,243,246],{},[111,244,245],{},"200は「中身がある」を意味しない。"," 794/794の全部グリーンを見て安心しかけたところに、本文が空のページが2件出てきた。HTTPコードだけを見る監視は、この種の障害を一生報告しない。",[10,248,249],{},"量の多い不具合ほど、原因が1箇所にまとまっていることがある。alt欠落154画像の正体は、共通コンポーネントの1文字だった。",{"title":251,"searchDepth":252,"depth":252,"links":253},"",2,[254,255,256,257,258,259,260,261,262,263,264,265],{"id":19,"depth":252,"text":19},{"id":31,"depth":252,"text":31},{"id":40,"depth":252,"text":40},{"id":52,"depth":252,"text":52},{"id":61,"depth":252,"text":62},{"id":80,"depth":252,"text":80},{"id":99,"depth":252,"text":100},{"id":120,"depth":252,"text":121},{"id":177,"depth":252,"text":178},{"id":199,"depth":252,"text":199},{"id":208,"depth":252,"text":208},{"id":237,"depth":252,"text":237},"dev",null,"旧Nuxt 2アプリをHerokuからCloudflare Pagesへ切り替えた1日の記録。DNSを触る前にUdemy講座のリンク62本を別ドメインへ移し、切替後はCodex 2並列でUI全面テスト。794/794が200なのに本文が空のページが2件見つかった。","md",{},"/app-cutover-udemy-links-finish","eurekapu",false,"2026-07-28T00:00:00.000Z",{"title":5,"description":268},"2026-07/2026-07-28/app-cutover-udemy-links-finish",[278,279,280,281,282],"Cloudflare Pages","Heroku","静的サイト","DNS切替","Udemy","-GlIqf7XJMavYkgxLqROb_BnGVL1kMGS6TbbmacSNlQ",[],[286,290,294,297,300],{"title":287,"path":288,"publishedAt":289},"Herokuを止められるか、nuxt generate を4回回して確かめた — 旧Nuxt 2アプリ静的化の実測","/app-static-build-heroku-retirement","2026-07-27T00:00:00.000Z",{"title":291,"path":292,"publishedAt":293},"2026年3月4日の開発日記 - 鍵屋サイトを一日で作り替えてPageSpeed94点まで磨いた","/2026-03-04-diary","2026-03-04T00:00:00.000Z",{"title":295,"path":296,"publishedAt":293},"Nuxt 2サイトをバニラHTML静的サイトに作り替えてCloudflare Pagesへ移行した","/static-site-rebuild-cloudflare-2026-03-04",{"title":298,"path":299,"publishedAt":289},"移行サイトのSEO・AI-SEO改善を1日で回した — 画像altを480件、記事の要点を203本、OG画像を署名付きに","/wordpress-nuxt-ai-seo-phase-c",{"title":301,"path":302,"publishedAt":289},"WordPress移行で画像3,810件が配信不能になった — 原本の発掘とCloudflare R2への復旧","/wp-uploads-outage-r2-recovery","https://log.eurekapu.com/og/blog/app-cutover-udemy-links-finish.png?v=2026-07-28T00%3A00%3A00.000Z&title=Heroku%E3%82%92%E6%AD%A2%E3%82%81%E3%82%8B%E5%89%8D%E3%81%AB%E5%B0%8E%E7%B7%9A%E3%82%92%E7%A7%BB%E3%81%99%20%E2%80%94%20app.eurekapu.com%20%E9%9D%99%E7%9A%84%E5%8C%96%E3%81%AE%E6%9C%AC%E7%95%AA%E5%88%87%E6%9B%BF%E3%81%A8Udemy%E3%83%AA%E3%83%B3%E3%82%AF62%E6%9C%AC%E3%81%AE%E7%A7%BB%E8%A8%AD&author=Kei%20Komatsu&sig=54b2e1dee90ed750",1785380006065]