[{"data":1,"prerenderedAt":1907},["ShallowReactive",2],{"content-/og-image-workers-migration":3,"all-pages-for-dir":1884,"related-/og-image-workers-migration":1885,"og-image-/og-image-workers-migration":1906},{"id":4,"title":5,"body":6,"category":1868,"concepts":1868,"description":1869,"extension":1870,"meta":1871,"navigation":273,"ogImage":1868,"path":1872,"project_name":1868,"published":1873,"publishedAt":1874,"seo":1875,"source":1868,"stem":1876,"tags":1877,"todo":1868,"unpublished":1873,"updatedAt":1868,"__hash__":1883},"pages/2025-12/2025-12-30/og-image-workers-migration.md","OGイメージの Cloudflare Workers 化検討",{"type":7,"value":8,"toc":1837},"minimark",[9,13,18,27,31,91,95,107,112,122,129,132,135,142,145,189,192,195,199,206,212,221,370,374,377,385,528,536,550,553,557,560,565,959,963,1122,1126,1206,1210,1213,1367,1371,1374,1392,1395,1444,1447,1450,1455,1521,1529,1533,1538,1541,1544,1620,1623,1647,1649,1652,1656,1750,1754,1780,1783,1802,1804,1807,1833],[10,11,5],"h1",{"id":12},"ogイメージの-cloudflare-workers-化検討",[14,15,17],"h2",{"id":16},"結論-見送り","結論: 見送り",[19,20,21,22,26],"p",{},"検討の結果",[23,24,25],"strong",{},"現在の静的生成方式を維持する","ことにした。",[28,29,30],"h3",{"id":30},"理由",[32,33,34,49,62,78],"ol",{},[35,36,37,40],"li",{},[23,38,39],{},"現在の構成で十分機能している",[41,42,43,46],"ul",{},[35,44,45],{},"ビルド時間100秒は許容範囲",[35,47,48],{},"55MBのdistサイズもCDN配信なら問題なし",[35,50,51,54],{},[23,52,53],{},"OGイメージへのアクセスは実際少ない",[41,55,56,59],{},[35,57,58],{},"SNSシェア時のクローラーのみがアクセス",[35,60,61],{},"1日1万アクセス程度のサイトでは、OGイメージアクセスはごく一部",[35,63,64,67],{},[23,65,66],{},"静的生成のメリットが大きい",[41,68,69,72,75],{},[35,70,71],{},"ビルド時に全画像がCDNに配置済み",[35,73,74],{},"アクセス時のレイテンシがほぼゼロ",[35,76,77],{},"追加のWorkers運用コストなし",[35,79,80,83],{},[23,81,82],{},"複雑さを増やすより、シンプルな構成を維持",[41,84,85,88],{},[35,86,87],{},"Workers化は「やれば速くなる」けど「やらなくても困らない」最適化",[35,89,90],{},"メンテナンスコストを考えると現状維持が妥当",[28,92,94],{"id":93},"例外-日本語クイズのogイメージ","例外: 日本語クイズのOGイメージ",[19,96,97,98,102,103,106],{},"日本語クイズ（",[99,100,101],"code",{},"/japanese-writing-quiz/","）のOGイメージは ",[23,104,105],{},"Workers で動的生成"," している。",[19,108,109,111],{},[23,110,30],{},": クイズ結果（正解数/問題数）がパラメータで動的に変わるため、静的生成では対応できない。",[113,114,119],"pre",{"className":115,"code":117,"language":118},[116],"language-text","/og/japanese-quiz?category=xxx&correct=5&total=10\n","text",[99,120,117],{"__ignoreMap":121},"",[19,123,124,125,128],{},"このように",[23,126,127],{},"動的なパラメータが必要なケース","では Workers 化が有効。",[130,131],"hr",{},[14,133,134],{"id":134},"背景",[19,136,137,138,141],{},"現在OGイメージは ",[99,139,140],{},"nuxt-og-image"," で静的生成しており、ビルド時間とデプロイサイズに大きな影響を与えている。",[28,143,144],{"id":144},"現状の問題",[146,147,148,161],"table",{},[149,150,151],"thead",{},[152,153,154,158],"tr",{},[155,156,157],"th",{},"項目",[155,159,160],{},"値",[162,163,164,173,181],"tbody",{},[152,165,166,170],{},[167,168,169],"td",{},"OGイメージ数",[167,171,172],{},"359枚",[152,174,175,178],{},[167,176,177],{},"OGイメージサイズ",[167,179,180],{},"55MB（dist全体の66%）",[152,182,183,186],{},[167,184,185],{},"推定生成時間",[167,187,188],{},"100秒（ビルド時間の52%）",[19,190,191],{},"特にJリーグクラブページは60クラブ × 複数年度のバリエーションがあり、OGイメージ数が多い。",[14,193,194],{"id":194},"現在のアーキテクチャ",[28,196,198],{"id":197},"パターン1-workers-動的生成日本語クイズのみ","パターン1: Workers 動的生成（日本語クイズのみ）",[19,200,201,202,205],{},"既に ",[99,203,204],{},"apps/workers/og/"," で動的生成を実装済み。",[113,207,210],{"className":208,"code":209,"language":118},[116],"/og/japanese-quiz?category=xxx&correct=5&total=10\n    ↓\nCloudflare Workers\n    ↓\nworkers-og でPNG生成\n",[99,211,209],{"__ignoreMap":121},[19,213,214,217,218],{},[23,215,216],{},"実装",": ",[99,219,220],{},"apps/workers/og/src/index.ts",[113,222,226],{"className":223,"code":224,"language":225,"meta":121,"style":121},"language-typescript shiki shiki-themes vitesse-light vitesse-light","import { ImageResponse, loadGoogleFont } from 'workers-og'\n\nreturn new ImageResponse(html, {\n  width: 1200,\n  height: 630,\n  fonts: [{ name: 'Noto Sans JP', data: fontData }]\n})\n","typescript",[99,227,228,268,275,299,315,328,364],{"__ignoreMap":121},[229,230,233,237,241,245,248,251,254,257,261,265],"span",{"class":231,"line":232},"line",1,[229,234,236],{"class":235},"sHkkW","import",[229,238,240],{"class":239},"shFtX"," {",[229,242,244],{"class":243},"s4oTP"," ImageResponse",[229,246,247],{"class":239},",",[229,249,250],{"class":243}," loadGoogleFont",[229,252,253],{"class":239}," }",[229,255,256],{"class":235}," from",[229,258,260],{"class":259},"sMJiu"," '",[229,262,264],{"class":263},"sdGka","workers-og",[229,266,267],{"class":259},"'\n",[229,269,271],{"class":231,"line":270},2,[229,272,274],{"emptyLinePlaceholder":273},true,"\n",[229,276,278,281,285,288,291,294,296],{"class":231,"line":277},3,[229,279,280],{"class":235},"return",[229,282,284],{"class":283},"stQ0i"," new",[229,286,244],{"class":287},"senZ8",[229,289,290],{"class":239},"(",[229,292,293],{"class":243},"html",[229,295,247],{"class":239},[229,297,298],{"class":239}," {\n",[229,300,302,306,308,312],{"class":231,"line":301},4,[229,303,305],{"class":304},"sz8Xr","  width",[229,307,217],{"class":239},[229,309,311],{"class":310},"sM54T","1200",[229,313,314],{"class":239},",\n",[229,316,318,321,323,326],{"class":231,"line":317},5,[229,319,320],{"class":304},"  height",[229,322,217],{"class":239},[229,324,325],{"class":310},"630",[229,327,314],{"class":239},[229,329,331,334,337,340,342,345,348,350,353,356,358,361],{"class":231,"line":330},6,[229,332,333],{"class":304},"  fonts",[229,335,336],{"class":239},": [{ ",[229,338,339],{"class":304},"name",[229,341,217],{"class":239},[229,343,344],{"class":259},"'",[229,346,347],{"class":263},"Noto Sans JP",[229,349,344],{"class":259},[229,351,352],{"class":239},", ",[229,354,355],{"class":304},"data",[229,357,217],{"class":239},[229,359,360],{"class":243},"fontData",[229,362,363],{"class":239}," }]\n",[229,365,367],{"class":231,"line":366},7,[229,368,369],{"class":239},"})\n",[28,371,373],{"id":372},"パターン2-nuxt-og-image-静的生成その他全ページ","パターン2: nuxt-og-image 静的生成（その他全ページ）",[19,375,376],{},"ビルド時に全ページのOGイメージをプリレンダリング。",[19,378,379,217,382],{},[23,380,381],{},"設定",[99,383,384],{},"nuxt.config.ts",[113,386,388],{"className":223,"code":387,"language":225,"meta":121,"style":121},"ogImage: {\n  defaults: { width: 1200, height: 630, renderer: 'satori' },\n  fonts: ['Noto+Sans+JP:400', 'Noto+Sans+JP:700'],\n  compatibility: {\n    prerender: { satori: 'node', resvg: 'node' }\n  }\n}\n",[99,389,390,400,444,472,481,518,523],{"__ignoreMap":121},[229,391,392,395,398],{"class":231,"line":232},[229,393,394],{"class":287},"ogImage",[229,396,397],{"class":239},":",[229,399,298],{"class":239},[229,401,402,405,407,409,412,414,417,419,422,424,427,429,432,434,436,439,441],{"class":231,"line":270},[229,403,404],{"class":287},"  defaults",[229,406,397],{"class":239},[229,408,240],{"class":239},[229,410,411],{"class":287}," width",[229,413,397],{"class":239},[229,415,416],{"class":310}," 1200",[229,418,247],{"class":239},[229,420,421],{"class":287}," height",[229,423,397],{"class":239},[229,425,426],{"class":310}," 630",[229,428,247],{"class":239},[229,430,431],{"class":287}," renderer",[229,433,397],{"class":239},[229,435,260],{"class":259},[229,437,438],{"class":263},"satori",[229,440,344],{"class":259},[229,442,443],{"class":239}," },\n",[229,445,446,448,450,453,455,458,460,462,464,467,469],{"class":231,"line":277},[229,447,333],{"class":287},[229,449,397],{"class":239},[229,451,452],{"class":239}," [",[229,454,344],{"class":259},[229,456,457],{"class":263},"Noto+Sans+JP:400",[229,459,344],{"class":259},[229,461,247],{"class":239},[229,463,260],{"class":259},[229,465,466],{"class":263},"Noto+Sans+JP:700",[229,468,344],{"class":259},[229,470,471],{"class":239},"],\n",[229,473,474,477,479],{"class":231,"line":301},[229,475,476],{"class":287},"  compatibility",[229,478,397],{"class":239},[229,480,298],{"class":239},[229,482,483,486,488,490,493,495,497,500,502,504,507,509,511,513,515],{"class":231,"line":317},[229,484,485],{"class":287},"    prerender",[229,487,397],{"class":239},[229,489,240],{"class":239},[229,491,492],{"class":287}," satori",[229,494,397],{"class":239},[229,496,260],{"class":259},[229,498,499],{"class":263},"node",[229,501,344],{"class":259},[229,503,247],{"class":239},[229,505,506],{"class":287}," resvg",[229,508,397],{"class":239},[229,510,260],{"class":259},[229,512,499],{"class":263},[229,514,344],{"class":259},[229,516,517],{"class":239}," }\n",[229,519,520],{"class":231,"line":330},[229,521,522],{"class":239},"  }\n",[229,524,525],{"class":231,"line":366},[229,526,527],{"class":239},"}\n",[19,529,530,217,533],{},[23,531,532],{},"コンポーネント",[99,534,535],{},"app/components/OgImage/",[41,537,538,544],{},[35,539,540,543],{},[99,541,542],{},"OgImageTemplate.vue"," - 汎用テンプレート",[35,545,546,549],{},[99,547,548],{},"JLeagueOgImage.vue"," - Jリーグクラブ用",[14,551,552],{"id":552},"移行計画",[28,554,556],{"id":555},"フェーズ1-jリーグクラブページの-workers-化","フェーズ1: Jリーグクラブページの Workers 化",[19,558,559],{},"最もバリエーションが多いJリーグクラブページから開始。",[561,562,564],"h4",{"id":563},"_1-1-workers-に-jリーグ用エンドポイント追加","1-1. Workers に Jリーグ用エンドポイント追加",[113,566,568],{"className":223,"code":567,"language":225,"meta":121,"style":121},"// apps/workers/og/src/index.ts に追加\n\nif (url.pathname === '/og/jleague') {\n  return handleJLeagueImage(url)\n}\n\nasync function handleJLeagueImage(url: URL): Promise\u003CResponse> {\n  const clubName = url.searchParams.get('club') || ''\n  const clubColor = url.searchParams.get('color') || '#1a1a2e'\n\n  const html = `\n  \u003Cdiv style=\"width: 1200px; height: 630px; display: flex; flex-direction: column;\n    justify-content: center; align-items: center;\n    background: linear-gradient(135deg, ${clubColor} 0%, #1a1a2e 100%);\n    font-family: Noto Sans JP, sans-serif;\">\n    \u003C!-- SVGシールドアイコン -->\n    \u003Csvg ...>...\u003C/svg>\n    \u003Cdiv style=\"font-size: 64px; font-weight: 700; color: white;\">\n      ${clubName}\n    \u003C/div>\n    \u003Cdiv style=\"font-size: 32px; color: rgba(255,255,255,0.9); margin-top: 24px;\">\n      財務データ分析\n    \u003C/div>\n  \u003C/div>\n  `\n\n  return new ImageResponse(html, {\n    width: 1200,\n    height: 630,\n    fonts: [{ name: 'Noto Sans JP', data: await getFontData() }]\n  })\n}\n",[99,569,570,576,580,612,627,631,635,672,714,754,759,771,777,783,800,806,812,818,824,834,840,846,852,857,863,869,874,891,903,915,948,954],{"__ignoreMap":121},[229,571,572],{"class":231,"line":232},[229,573,575],{"class":574},"sxvE3","// apps/workers/og/src/index.ts に追加\n",[229,577,578],{"class":231,"line":270},[229,579,274],{"emptyLinePlaceholder":273},[229,581,582,585,588,591,594,597,600,602,605,607,610],{"class":231,"line":277},[229,583,584],{"class":235},"if",[229,586,587],{"class":239}," (",[229,589,590],{"class":243},"url",[229,592,593],{"class":239},".",[229,595,596],{"class":243},"pathname",[229,598,599],{"class":283}," ===",[229,601,260],{"class":259},[229,603,604],{"class":263},"/og/jleague",[229,606,344],{"class":259},[229,608,609],{"class":239},")",[229,611,298],{"class":239},[229,613,614,617,620,622,624],{"class":231,"line":301},[229,615,616],{"class":235},"  return",[229,618,619],{"class":287}," handleJLeagueImage",[229,621,290],{"class":239},[229,623,590],{"class":243},[229,625,626],{"class":239},")\n",[229,628,629],{"class":231,"line":317},[229,630,527],{"class":239},[229,632,633],{"class":231,"line":330},[229,634,274],{"emptyLinePlaceholder":273},[229,636,637,640,643,645,647,649,651,655,658,661,664,667,670],{"class":231,"line":366},[229,638,639],{"class":283},"async",[229,641,642],{"class":283}," function",[229,644,619],{"class":287},[229,646,290],{"class":239},[229,648,590],{"class":243},[229,650,217],{"class":239},[229,652,654],{"class":653},"sSkh3","URL",[229,656,657],{"class":239},"):",[229,659,660],{"class":653}," Promise",[229,662,663],{"class":239},"\u003C",[229,665,666],{"class":653},"Response",[229,668,669],{"class":239},">",[229,671,298],{"class":239},[229,673,675,678,681,684,687,689,692,694,697,699,701,704,706,708,711],{"class":231,"line":674},8,[229,676,677],{"class":283},"  const ",[229,679,680],{"class":243},"clubName",[229,682,683],{"class":239}," =",[229,685,686],{"class":243}," url",[229,688,593],{"class":239},[229,690,691],{"class":243},"searchParams",[229,693,593],{"class":239},[229,695,696],{"class":287},"get",[229,698,290],{"class":239},[229,700,344],{"class":259},[229,702,703],{"class":263},"club",[229,705,344],{"class":259},[229,707,609],{"class":239},[229,709,710],{"class":283}," || ",[229,712,713],{"class":259},"''\n",[229,715,717,719,722,724,726,728,730,732,734,736,738,741,743,745,747,749,752],{"class":231,"line":716},9,[229,718,677],{"class":283},[229,720,721],{"class":243},"clubColor",[229,723,683],{"class":239},[229,725,686],{"class":243},[229,727,593],{"class":239},[229,729,691],{"class":243},[229,731,593],{"class":239},[229,733,696],{"class":287},[229,735,290],{"class":239},[229,737,344],{"class":259},[229,739,740],{"class":263},"color",[229,742,344],{"class":259},[229,744,609],{"class":239},[229,746,710],{"class":283},[229,748,344],{"class":259},[229,750,751],{"class":263},"#1a1a2e",[229,753,267],{"class":259},[229,755,757],{"class":231,"line":756},10,[229,758,274],{"emptyLinePlaceholder":273},[229,760,762,764,766,768],{"class":231,"line":761},11,[229,763,677],{"class":283},[229,765,293],{"class":243},[229,767,683],{"class":239},[229,769,770],{"class":259}," `\n",[229,772,774],{"class":231,"line":773},12,[229,775,776],{"class":263},"  \u003Cdiv style=\"width: 1200px; height: 630px; display: flex; flex-direction: column;\n",[229,778,780],{"class":231,"line":779},13,[229,781,782],{"class":263},"    justify-content: center; align-items: center;\n",[229,784,786,789,792,794,797],{"class":231,"line":785},14,[229,787,788],{"class":263},"    background: linear-gradient(135deg, ",[229,790,791],{"class":235},"${",[229,793,721],{"class":263},[229,795,796],{"class":235},"}",[229,798,799],{"class":263}," 0%, #1a1a2e 100%);\n",[229,801,803],{"class":231,"line":802},15,[229,804,805],{"class":263},"    font-family: Noto Sans JP, sans-serif;\">\n",[229,807,809],{"class":231,"line":808},16,[229,810,811],{"class":263},"    \u003C!-- SVGシールドアイコン -->\n",[229,813,815],{"class":231,"line":814},17,[229,816,817],{"class":263},"    \u003Csvg ...>...\u003C/svg>\n",[229,819,821],{"class":231,"line":820},18,[229,822,823],{"class":263},"    \u003Cdiv style=\"font-size: 64px; font-weight: 700; color: white;\">\n",[229,825,827,830,832],{"class":231,"line":826},19,[229,828,829],{"class":235},"      ${",[229,831,680],{"class":263},[229,833,527],{"class":235},[229,835,837],{"class":231,"line":836},20,[229,838,839],{"class":263},"    \u003C/div>\n",[229,841,843],{"class":231,"line":842},21,[229,844,845],{"class":263},"    \u003Cdiv style=\"font-size: 32px; color: rgba(255,255,255,0.9); margin-top: 24px;\">\n",[229,847,849],{"class":231,"line":848},22,[229,850,851],{"class":263},"      財務データ分析\n",[229,853,855],{"class":231,"line":854},23,[229,856,839],{"class":263},[229,858,860],{"class":231,"line":859},24,[229,861,862],{"class":263},"  \u003C/div>\n",[229,864,866],{"class":231,"line":865},25,[229,867,868],{"class":259},"  `\n",[229,870,872],{"class":231,"line":871},26,[229,873,274],{"emptyLinePlaceholder":273},[229,875,877,879,881,883,885,887,889],{"class":231,"line":876},27,[229,878,616],{"class":235},[229,880,284],{"class":283},[229,882,244],{"class":287},[229,884,290],{"class":239},[229,886,293],{"class":243},[229,888,247],{"class":239},[229,890,298],{"class":239},[229,892,894,897,899,901],{"class":231,"line":893},28,[229,895,896],{"class":304},"    width",[229,898,217],{"class":239},[229,900,311],{"class":310},[229,902,314],{"class":239},[229,904,906,909,911,913],{"class":231,"line":905},29,[229,907,908],{"class":304},"    height",[229,910,217],{"class":239},[229,912,325],{"class":310},[229,914,314],{"class":239},[229,916,918,921,923,925,927,929,931,933,935,937,939,942,945],{"class":231,"line":917},30,[229,919,920],{"class":304},"    fonts",[229,922,336],{"class":239},[229,924,339],{"class":304},[229,926,217],{"class":239},[229,928,344],{"class":259},[229,930,347],{"class":263},[229,932,344],{"class":259},[229,934,352],{"class":239},[229,936,355],{"class":304},[229,938,217],{"class":239},[229,940,941],{"class":235},"await",[229,943,944],{"class":287}," getFontData",[229,946,947],{"class":239},"() }]\n",[229,949,951],{"class":231,"line":950},31,[229,952,953],{"class":239},"  })\n",[229,955,957],{"class":231,"line":956},32,[229,958,527],{"class":239},[561,960,962],{"id":961},"_1-2-nuxt側でogイメージurlを変更","1-2. Nuxt側でOGイメージURLを変更",[113,964,968],{"className":965,"code":966,"language":967,"meta":121,"style":121},"language-vue shiki shiki-themes vitesse-light vitesse-light","\u003C!-- pages/financial-quiz/jleague/club/[id].vue -->\n\u003Cscript setup>\nconst club = ... // クラブデータ取得\n\nuseHead({\n  meta: [\n    {\n      property: 'og:image',\n      content: `https://og-image-worker.number55number55.workers.dev/og/jleague?club=${encodeURIComponent(club.name)}&color=${encodeURIComponent(club.color)}`\n    }\n  ]\n})\n\u003C/script>\n","vue",[99,969,970,975,988,1004,1008,1016,1026,1031,1047,1099,1104,1109,1113],{"__ignoreMap":121},[229,971,972],{"class":231,"line":232},[229,973,974],{"class":574},"\u003C!-- pages/financial-quiz/jleague/club/[id].vue -->\n",[229,976,977,979,982,985],{"class":231,"line":270},[229,978,663],{"class":239},[229,980,981],{"class":235},"script",[229,983,984],{"class":243}," setup",[229,986,987],{"class":239},">\n",[229,989,990,993,996,998,1001],{"class":231,"line":277},[229,991,992],{"class":283},"const",[229,994,995],{"class":243}," club",[229,997,683],{"class":239},[229,999,1000],{"class":239}," ...",[229,1002,1003],{"class":574}," // クラブデータ取得\n",[229,1005,1006],{"class":231,"line":301},[229,1007,274],{"emptyLinePlaceholder":273},[229,1009,1010,1013],{"class":231,"line":317},[229,1011,1012],{"class":287},"useHead",[229,1014,1015],{"class":239},"({\n",[229,1017,1018,1021,1023],{"class":231,"line":330},[229,1019,1020],{"class":304},"  meta",[229,1022,397],{"class":239},[229,1024,1025],{"class":239}," [\n",[229,1027,1028],{"class":231,"line":366},[229,1029,1030],{"class":239},"    {\n",[229,1032,1033,1036,1038,1040,1043,1045],{"class":231,"line":674},[229,1034,1035],{"class":304},"      property",[229,1037,397],{"class":239},[229,1039,260],{"class":259},[229,1041,1042],{"class":263},"og:image",[229,1044,344],{"class":259},[229,1046,314],{"class":239},[229,1048,1049,1052,1054,1057,1060,1062,1065,1067,1069,1071,1073,1075,1077,1080,1082,1084,1086,1088,1090,1092,1094,1096],{"class":231,"line":716},[229,1050,1051],{"class":304},"      content",[229,1053,397],{"class":239},[229,1055,1056],{"class":259}," `",[229,1058,1059],{"class":263},"https://og-image-worker.number55number55.workers.dev/og/jleague?club=",[229,1061,791],{"class":235},[229,1063,1064],{"class":287},"encodeURIComponent",[229,1066,290],{"class":239},[229,1068,703],{"class":263},[229,1070,593],{"class":239},[229,1072,339],{"class":263},[229,1074,609],{"class":239},[229,1076,796],{"class":235},[229,1078,1079],{"class":263},"&color=",[229,1081,791],{"class":235},[229,1083,1064],{"class":287},[229,1085,290],{"class":239},[229,1087,703],{"class":263},[229,1089,593],{"class":239},[229,1091,740],{"class":263},[229,1093,609],{"class":239},[229,1095,796],{"class":235},[229,1097,1098],{"class":259},"`\n",[229,1100,1101],{"class":231,"line":756},[229,1102,1103],{"class":239},"    }\n",[229,1105,1106],{"class":231,"line":761},[229,1107,1108],{"class":239},"  ]\n",[229,1110,1111],{"class":231,"line":773},[229,1112,369],{"class":239},[229,1114,1115,1118,1120],{"class":231,"line":779},[229,1116,1117],{"class":239},"\u003C/",[229,1119,981],{"class":235},[229,1121,987],{"class":239},[561,1123,1125],{"id":1124},"_1-3-nuxt-og-image-からjリーグページを除外","1-3. nuxt-og-image からJリーグページを除外",[113,1127,1129],{"className":223,"code":1128,"language":225,"meta":121,"style":121},"// nuxt.config.ts\nnitro: {\n  prerender: {\n    ignore: [\n      /\\/financial-quiz\\/jleague\\/club\\//  // Workers で生成\n    ]\n  }\n}\n",[99,1130,1131,1136,1145,1154,1163,1193,1198,1202],{"__ignoreMap":121},[229,1132,1133],{"class":231,"line":232},[229,1134,1135],{"class":574},"// nuxt.config.ts\n",[229,1137,1138,1141,1143],{"class":231,"line":270},[229,1139,1140],{"class":287},"nitro",[229,1142,397],{"class":239},[229,1144,298],{"class":239},[229,1146,1147,1150,1152],{"class":231,"line":277},[229,1148,1149],{"class":287},"  prerender",[229,1151,397],{"class":239},[229,1153,298],{"class":239},[229,1155,1156,1159,1161],{"class":231,"line":301},[229,1157,1158],{"class":287},"    ignore",[229,1160,397],{"class":239},[229,1162,1025],{"class":239},[229,1164,1165,1168,1172,1176,1178,1181,1183,1185,1187,1190],{"class":231,"line":317},[229,1166,1167],{"class":259},"      /",[229,1169,1171],{"class":1170},"svWSF","\\/",[229,1173,1175],{"class":1174},"sLkVR","financial-quiz",[229,1177,1171],{"class":1170},[229,1179,1180],{"class":1174},"jleague",[229,1182,1171],{"class":1170},[229,1184,703],{"class":1174},[229,1186,1171],{"class":1170},[229,1188,1189],{"class":259},"/",[229,1191,1192],{"class":574},"  // Workers で生成\n",[229,1194,1195],{"class":231,"line":330},[229,1196,1197],{"class":239},"    ]\n",[229,1199,1200],{"class":231,"line":366},[229,1201,522],{"class":239},[229,1203,1204],{"class":231,"line":674},[229,1205,527],{"class":239},[28,1207,1209],{"id":1208},"フェーズ2-汎用テンプレートの-workers-化","フェーズ2: 汎用テンプレートの Workers 化",[19,1211,1212],{},"ブログ記事など、汎用的なOGイメージも Workers 化。",[113,1214,1216],{"className":223,"code":1215,"language":225,"meta":121,"style":121},"// /og/article?title=xxx&date=2025-12-30\nasync function handleArticleImage(url: URL): Promise\u003CResponse> {\n  const title = url.searchParams.get('title') || ''\n  const date = url.searchParams.get('date') || ''\n\n  // OgImageTemplate.vue と同等のHTMLを生成\n  const html = `...`\n\n  return new ImageResponse(html, { ... })\n}\n",[99,1217,1218,1223,1252,1285,1318,1322,1327,1342,1346,1363],{"__ignoreMap":121},[229,1219,1220],{"class":231,"line":232},[229,1221,1222],{"class":574},"// /og/article?title=xxx&date=2025-12-30\n",[229,1224,1225,1227,1229,1232,1234,1236,1238,1240,1242,1244,1246,1248,1250],{"class":231,"line":270},[229,1226,639],{"class":283},[229,1228,642],{"class":283},[229,1230,1231],{"class":287}," handleArticleImage",[229,1233,290],{"class":239},[229,1235,590],{"class":243},[229,1237,217],{"class":239},[229,1239,654],{"class":653},[229,1241,657],{"class":239},[229,1243,660],{"class":653},[229,1245,663],{"class":239},[229,1247,666],{"class":653},[229,1249,669],{"class":239},[229,1251,298],{"class":239},[229,1253,1254,1256,1259,1261,1263,1265,1267,1269,1271,1273,1275,1277,1279,1281,1283],{"class":231,"line":277},[229,1255,677],{"class":283},[229,1257,1258],{"class":243},"title",[229,1260,683],{"class":239},[229,1262,686],{"class":243},[229,1264,593],{"class":239},[229,1266,691],{"class":243},[229,1268,593],{"class":239},[229,1270,696],{"class":287},[229,1272,290],{"class":239},[229,1274,344],{"class":259},[229,1276,1258],{"class":263},[229,1278,344],{"class":259},[229,1280,609],{"class":239},[229,1282,710],{"class":283},[229,1284,713],{"class":259},[229,1286,1287,1289,1292,1294,1296,1298,1300,1302,1304,1306,1308,1310,1312,1314,1316],{"class":231,"line":301},[229,1288,677],{"class":283},[229,1290,1291],{"class":243},"date",[229,1293,683],{"class":239},[229,1295,686],{"class":243},[229,1297,593],{"class":239},[229,1299,691],{"class":243},[229,1301,593],{"class":239},[229,1303,696],{"class":287},[229,1305,290],{"class":239},[229,1307,344],{"class":259},[229,1309,1291],{"class":263},[229,1311,344],{"class":259},[229,1313,609],{"class":239},[229,1315,710],{"class":283},[229,1317,713],{"class":259},[229,1319,1320],{"class":231,"line":317},[229,1321,274],{"emptyLinePlaceholder":273},[229,1323,1324],{"class":231,"line":330},[229,1325,1326],{"class":574},"  // OgImageTemplate.vue と同等のHTMLを生成\n",[229,1328,1329,1331,1333,1335,1337,1340],{"class":231,"line":366},[229,1330,677],{"class":283},[229,1332,293],{"class":243},[229,1334,683],{"class":239},[229,1336,1056],{"class":259},[229,1338,1339],{"class":263},"...",[229,1341,1098],{"class":259},[229,1343,1344],{"class":231,"line":674},[229,1345,274],{"emptyLinePlaceholder":273},[229,1347,1348,1350,1352,1354,1356,1358,1360],{"class":231,"line":716},[229,1349,616],{"class":235},[229,1351,284],{"class":283},[229,1353,244],{"class":287},[229,1355,290],{"class":239},[229,1357,293],{"class":243},[229,1359,247],{"class":239},[229,1361,1362],{"class":239}," { ... })\n",[229,1364,1365],{"class":231,"line":756},[229,1366,527],{"class":239},[28,1368,1370],{"id":1369},"フェーズ3-nuxt-og-image-の完全削除","フェーズ3: nuxt-og-image の完全削除",[19,1372,1373],{},"全てのOGイメージを Workers 化した後：",[32,1375,1376,1384,1389],{},[35,1377,1378,1380,1381,1383],{},[99,1379,384],{}," から ",[99,1382,140],{}," モジュールを削除",[35,1385,1386,1388],{},[99,1387,535],{}," を削除",[35,1390,1391],{},"ビルドサイズが 55MB 削減",[14,1393,1394],{"id":1394},"期待効果",[146,1396,1397,1409],{},[149,1398,1399],{},[152,1400,1401,1403,1406],{},[155,1402,157],{},[155,1404,1405],{},"Before",[155,1407,1408],{},"After",[162,1410,1411,1422,1433],{},[152,1412,1413,1416,1419],{},[167,1414,1415],{},"OGイメージ生成時間",[167,1417,1418],{},"100秒",[167,1420,1421],{},"0秒",[152,1423,1424,1427,1430],{},[167,1425,1426],{},"dist サイズ",[167,1428,1429],{},"83MB",[167,1431,1432],{},"28MB",[152,1434,1435,1438,1441],{},[167,1436,1437],{},"OGイメージ更新",[167,1439,1440],{},"再ビルド必要",[167,1442,1443],{},"即時反映",[14,1445,1446],{"id":1446},"実装の注意点",[28,1448,1449],{"id":1449},"フォント読み込み",[19,1451,1452,1454],{},[99,1453,264],{}," では Google Fonts からフォントを動的にロードする：",[113,1456,1458],{"className":223,"code":1457,"language":225,"meta":121,"style":121},"const fontData = await loadGoogleFont({\n  family: 'Noto Sans JP',\n  weight: 400,\n  text: '使用する文字のサブセット'\n})\n",[99,1459,1460,1476,1491,1503,1517],{"__ignoreMap":121},[229,1461,1462,1465,1467,1469,1472,1474],{"class":231,"line":232},[229,1463,1464],{"class":283},"const ",[229,1466,360],{"class":243},[229,1468,683],{"class":239},[229,1470,1471],{"class":235}," await",[229,1473,250],{"class":287},[229,1475,1015],{"class":239},[229,1477,1478,1481,1483,1485,1487,1489],{"class":231,"line":270},[229,1479,1480],{"class":304},"  family",[229,1482,217],{"class":239},[229,1484,344],{"class":259},[229,1486,347],{"class":263},[229,1488,344],{"class":259},[229,1490,314],{"class":239},[229,1492,1493,1496,1498,1501],{"class":231,"line":277},[229,1494,1495],{"class":304},"  weight",[229,1497,217],{"class":239},[229,1499,1500],{"class":310},"400",[229,1502,314],{"class":239},[229,1504,1505,1508,1510,1512,1515],{"class":231,"line":301},[229,1506,1507],{"class":304},"  text",[229,1509,217],{"class":239},[229,1511,344],{"class":259},[229,1513,1514],{"class":263},"使用する文字のサブセット",[229,1516,267],{"class":259},[229,1518,1519],{"class":231,"line":317},[229,1520,369],{"class":239},[19,1522,1523,217,1526,1528],{},[23,1524,1525],{},"ポイント",[99,1527,118],{}," パラメータで使用文字を限定するとフォントサイズが小さくなり、レイテンシが改善。",[28,1530,1532],{"id":1531},"svg-の取り扱い","SVG の取り扱い",[19,1534,1535,1537],{},[99,1536,264],{}," は Satori ベースなので、SVG を直接 HTML に埋め込める。ただし、複雑なSVGは簡略化が必要な場合あり。",[28,1539,1540],{"id":1540},"キャッシュ戦略",[19,1542,1543],{},"Workers でキャッシュヘッダーを設定：",[113,1545,1547],{"className":223,"code":1546,"language":225,"meta":121,"style":121},"return new ImageResponse(html, {\n  width: 1200,\n  height: 630,\n  headers: {\n    'Cache-Control': 'public, max-age=86400, s-maxage=604800'\n  }\n})\n",[99,1548,1549,1565,1575,1585,1593,1612,1616],{"__ignoreMap":121},[229,1550,1551,1553,1555,1557,1559,1561,1563],{"class":231,"line":232},[229,1552,280],{"class":235},[229,1554,284],{"class":283},[229,1556,244],{"class":287},[229,1558,290],{"class":239},[229,1560,293],{"class":243},[229,1562,247],{"class":239},[229,1564,298],{"class":239},[229,1566,1567,1569,1571,1573],{"class":231,"line":270},[229,1568,305],{"class":304},[229,1570,217],{"class":239},[229,1572,311],{"class":310},[229,1574,314],{"class":239},[229,1576,1577,1579,1581,1583],{"class":231,"line":277},[229,1578,320],{"class":304},[229,1580,217],{"class":239},[229,1582,325],{"class":310},[229,1584,314],{"class":239},[229,1586,1587,1590],{"class":231,"line":301},[229,1588,1589],{"class":304},"  headers",[229,1591,1592],{"class":239},": {\n",[229,1594,1595,1598,1601,1603,1605,1607,1610],{"class":231,"line":317},[229,1596,1597],{"class":259},"    '",[229,1599,1600],{"class":263},"Cache-Control",[229,1602,344],{"class":259},[229,1604,217],{"class":239},[229,1606,344],{"class":259},[229,1608,1609],{"class":263},"public, max-age=86400, s-maxage=604800",[229,1611,267],{"class":259},[229,1613,1614],{"class":231,"line":330},[229,1615,522],{"class":239},[229,1617,1618],{"class":231,"line":366},[229,1619,369],{"class":239},[14,1621,1622],{"id":1622},"関連ファイル",[41,1624,1625,1630,1636,1641],{},[35,1626,1627,1629],{},[99,1628,220],{}," - 既存の Workers 実装",[35,1631,1632,1635],{},[99,1633,1634],{},"apps/web/app/components/OgImage/JLeagueOgImage.vue"," - 移植対象のVueコンポーネント",[35,1637,1638,543],{},[99,1639,1640],{},"apps/web/app/components/OgImage/OgImageTemplate.vue",[35,1642,1643,1646],{},[99,1644,1645],{},"apps/web/nuxt.config.ts"," - OGイメージ設定",[130,1648],{},[14,1650,1651],{"id":1651},"検討時の比較",[28,1653,1655],{"id":1654},"静的生成-vs-workers-動的生成","静的生成 vs Workers 動的生成",[146,1657,1658,1671],{},[149,1659,1660],{},[152,1661,1662,1665,1668],{},[155,1663,1664],{},"観点",[155,1666,1667],{},"静的生成（現在）",[155,1669,1670],{},"Workers 動的生成",[162,1672,1673,1686,1698,1711,1724,1737],{},[152,1674,1675,1680,1683],{},[167,1676,1677],{},[23,1678,1679],{},"アクセス時レイテンシ",[167,1681,1682],{},"ほぼ0ms（CDN直接配信）",[167,1684,1685],{},"初回50〜100ms、2回目以降0ms",[152,1687,1688,1693,1696],{},[167,1689,1690],{},[23,1691,1692],{},"ビルド時間",[167,1694,1695],{},"+100秒",[167,1697,1421],{},[152,1699,1700,1705,1708],{},[167,1701,1702],{},[23,1703,1704],{},"distサイズ",[167,1706,1707],{},"+55MB",[167,1709,1710],{},"0MB",[152,1712,1713,1718,1721],{},[167,1714,1715],{},[23,1716,1717],{},"キャッシュ",[167,1719,1720],{},"最初からCDNにある",[167,1722,1723],{},"初回アクセスで生成→CDNキャッシュ",[152,1725,1726,1731,1734],{},[167,1727,1728],{},[23,1729,1730],{},"運用コスト",[167,1732,1733],{},"なし",[167,1735,1736],{},"Workers の管理が必要",[152,1738,1739,1744,1747],{},[167,1740,1741],{},[23,1742,1743],{},"柔軟性",[167,1745,1746],{},"再ビルドが必要",[167,1748,1749],{},"パラメータで動的変更可能",[28,1751,1753],{"id":1752},"workers-化が有効なケース","Workers 化が有効なケース",[41,1755,1756,1762,1768,1774],{},[35,1757,1758,1761],{},[23,1759,1760],{},"頻繁にデプロイする","（1日に何度も）→ ビルド時間削減の恩恵大",[35,1763,1764,1767],{},[23,1765,1766],{},"OGイメージにリアルタイムデータを表示","（例：株価、スコア）",[35,1769,1770,1773],{},[23,1771,1772],{},"バリエーションが無限","（ユーザー生成コンテンツなど）",[35,1775,1776,1779],{},[23,1777,1778],{},"パラメータで動的に変化する","（例：クイズ結果）",[28,1781,1782],{"id":1782},"静的生成で十分なケース",[41,1784,1785,1791,1797],{},[35,1786,1787,1790],{},[23,1788,1789],{},"デプロイ頻度が低い","（週数回程度）",[35,1792,1793,1796],{},[23,1794,1795],{},"OGイメージの種類が有限","（ブログ記事、固定ページ）",[35,1798,1799],{},[23,1800,1801],{},"シンプルな構成を維持したい",[130,1803],{},[14,1805,1806],{"id":1806},"参考リンク",[41,1808,1809,1818,1826],{},[35,1810,1811,1817],{},[1812,1813,264],"a",{"href":1814,"rel":1815},"https://github.com/kvnang/workers-og",[1816],"nofollow"," - Cloudflare Workers 用 OG 画像生成ライブラリ",[35,1819,1820,1825],{},[1812,1821,1824],{"href":1822,"rel":1823},"https://github.com/vercel/satori",[1816],"Satori"," - HTML/CSS から SVG への変換ライブラリ",[35,1827,1828,1832],{},[1812,1829,140],{"href":1830,"rel":1831},"https://nuxtseo.com/og-image",[1816]," - Nuxt 用 OG 画像モジュール",[1834,1835,1836],"style",{},"html pre.shiki code .sHkkW, html code.shiki .sHkkW{--shiki-default:#1E754F;--shiki-dark:#1E754F}html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}html pre.shiki code .s4oTP, html code.shiki .s4oTP{--shiki-default:#B07D48;--shiki-dark:#B07D48}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .sz8Xr, html code.shiki .sz8Xr{--shiki-default:#998418;--shiki-dark:#998418}html pre.shiki code .sM54T, html code.shiki .sM54T{--shiki-default:#2F798A;--shiki-dark:#2F798A}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .sSkh3, html code.shiki .sSkh3{--shiki-default:#2E8F82;--shiki-dark:#2E8F82}html pre.shiki code .svWSF, html code.shiki .svWSF{--shiki-default:#BDA437;--shiki-dark:#BDA437}html pre.shiki code .sLkVR, html code.shiki .sLkVR{--shiki-default:#AB5E3F;--shiki-dark:#AB5E3F}",{"title":121,"searchDepth":270,"depth":270,"links":1838},[1839,1843,1846,1850,1855,1856,1861,1862,1867],{"id":16,"depth":270,"text":17,"children":1840},[1841,1842],{"id":30,"depth":277,"text":30},{"id":93,"depth":277,"text":94},{"id":134,"depth":270,"text":134,"children":1844},[1845],{"id":144,"depth":277,"text":144},{"id":194,"depth":270,"text":194,"children":1847},[1848,1849],{"id":197,"depth":277,"text":198},{"id":372,"depth":277,"text":373},{"id":552,"depth":270,"text":552,"children":1851},[1852,1853,1854],{"id":555,"depth":277,"text":556},{"id":1208,"depth":277,"text":1209},{"id":1369,"depth":277,"text":1370},{"id":1394,"depth":270,"text":1394},{"id":1446,"depth":270,"text":1446,"children":1857},[1858,1859,1860],{"id":1449,"depth":277,"text":1449},{"id":1531,"depth":277,"text":1532},{"id":1540,"depth":277,"text":1540},{"id":1622,"depth":270,"text":1622},{"id":1651,"depth":270,"text":1651,"children":1863},[1864,1865,1866],{"id":1654,"depth":277,"text":1655},{"id":1752,"depth":277,"text":1753},{"id":1782,"depth":277,"text":1782},{"id":1806,"depth":270,"text":1806},null,"nuxt-og-image での静的生成から Cloudflare Workers での動的生成への移行を検討し、見送りとした記録","md",{},"/og-image-workers-migration",false,"2025-12-30T00:00:00.000Z",{"title":5,"description":1869},"2025-12/2025-12-30/og-image-workers-migration",[1878,1879,1880,1881,1882],"og-image","cloudflare-workers","performance","build-optimization","decision","QJdTDXBudt8iGVBF7aKjvEpq0Z3jwrmF3hg2MNPVQeA",[],[1886,1890,1894,1898,1902],{"title":1887,"path":1888,"publishedAt":1889},"Better Auth認証基盤の本番投入と問題集機能6フェーズ拡張の記録","/eurekapu-auth-quiz-features-2026-02-21","2026-02-21T00:00:00.000Z",{"title":1891,"path":1892,"publishedAt":1893},"Cloudflare AccessとR2でコンセプトサイトのインフラを整備した","/oto-archive-infra-2026-02-16","2026-02-16T00:00:00.000Z",{"title":1895,"path":1896,"publishedAt":1897},"検索ページ（/search）パフォーマンス改善計画","/search-page-performance-plan","2026-01-29T00:00:00.000Z",{"title":1899,"path":1900,"publishedAt":1901},"Nuxt 4 + @nuxt/content の開発サーバー起動が遅い原因と対策","/nuxt-dev-startup-time","2026-01-10T00:00:00.000Z",{"title":1903,"path":1904,"publishedAt":1905},"Nuxtビルド最適化：341ページを137秒でSSG","/nuxt-devtools-optimization","2026-01-02T00:00:00.000Z","https://log.eurekapu.com/og/blog/og-image-workers-migration.png?v=2025-12-30T00%3A00%3A00.000Z&title=OG%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AE%20Cloudflare%20Workers%20%E5%8C%96%E6%A4%9C%E8%A8%8E&author=Kei%20Komatsu&sig=58c462fe79d8de1a",1785654978948]