[{"data":1,"prerenderedAt":3282},["ShallowReactive",2],{"content-/dynamic-ogp-image-generation-guide":3,"all-pages-for-dir":3261,"related-/dynamic-ogp-image-generation-guide":3262,"og-image-/dynamic-ogp-image-generation-guide":3281},{"id":4,"title":5,"body":6,"category":3246,"concepts":3246,"description":3247,"extension":3248,"meta":3249,"navigation":291,"ogImage":3246,"path":3250,"project_name":3246,"published":3251,"publishedAt":3252,"seo":3253,"source":3246,"stem":3254,"tags":3255,"todo":3246,"unpublished":3251,"updatedAt":3246,"__hash__":3260},"pages/2025-12/2025-12-24/dynamic-ogp-image-generation-guide.md","動的OGP画像生成の実装ガイド - Satori + Cloudflare Workers",{"type":7,"value":8,"toc":3224},"minimark",[9,14,18,22,29,32,36,114,119,135,137,140,151,153,157,161,167,173,190,195,241,243,247,252,562,567,578,580,584,589,595,600,625,630,636,638,642,647,652,663,813,815,819,824,830,835,843,848,860,865,871,874,879,960,962,966,971,1019,1024,1035,1040,1051,1053,1057,1062,1415,1420,1426,1428,1432,1444,1672,1678,1680,1684,1689,1736,1741,1753,1758,1769,1771,1775,1782,1933,1938,1997,2003,2005,2009,2014,2020,2031,2033,2037,3161,3163,3166,3217,3220],[10,11,13],"h1",{"id":12},"動的ogp画像生成の実装ガイド","動的OGP画像生成の実装ガイド",[15,16,17],"h2",{"id":17},"この記事の背景",[19,20,21],"p",{},"クイズアプリで「8/10問正解しました！」のような結果をSNSでシェアする際に、結果に応じたOGP画像を表示したい。しかしユーザーごとに結果が異なるため、静的な画像では対応できない。",[19,23,24,28],{},[25,26,27],"strong",{},"解決策",": リクエストパラメータに応じてOGP画像を動的に生成するAPIを実装する。",[30,31],"hr",{},[15,33,35],{"id":34},"静的ogp-vs-動的ogp","静的OGP vs 動的OGP",[37,38,39,55],"table",{},[40,41,42],"thead",{},[43,44,45,49,52],"tr",{},[46,47,48],"th",{},"項目",[46,50,51],{},"静的OGP",[46,53,54],{},"動的OGP",[56,57,58,70,81,92,103],"tbody",{},[43,59,60,64,67],{},[61,62,63],"td",{},"生成タイミング",[61,65,66],{},"ビルド時",[61,68,69],{},"リクエスト時",[43,71,72,75,78],{},[61,73,74],{},"画像の種類",[61,76,77],{},"固定（記事タイトル等）",[61,79,80],{},"可変（ユーザー結果等）",[43,82,83,86,89],{},[61,84,85],{},"パフォーマンス",[61,87,88],{},"最速（CDN配信）",[61,90,91],{},"やや遅い（生成処理）",[43,93,94,97,100],{},[61,95,96],{},"柔軟性",[61,98,99],{},"低い",[61,101,102],{},"高い",[43,104,105,108,111],{},[61,106,107],{},"例",[61,109,110],{},"ブログ記事のOGP",[61,112,113],{},"クイズ結果、プロフィールカード",[115,116,118],"h3",{"id":117},"いつ動的ogpが必要か","いつ動的OGPが必要か？",[120,121,122,126,129,132],"ul",{},[123,124,125],"li",{},"クイズ・テストの結果シェア",[123,127,128],{},"ユーザープロフィールカード",[123,130,131],{},"リアルタイムデータ（株価、天気など）",[123,133,134],{},"パーソナライズされたコンテンツ",[30,136],{},[15,138,139],{"id":139},"技術スタック",[141,142,147],"pre",{"className":143,"code":145,"language":146},[144],"language-text","┌─────────────────────────────────────────────────────────┐\n│  リクエスト                                              │\n│  /api/og?title=結果&score=8&total=10                    │\n└─────────────────────────────────────────────────────────┘\n                          ↓\n┌─────────────────────────────────────────────────────────┐\n│  Satori                                                  │\n│  JSX/React-like構文 → SVG に変換                         │\n│  （HTML/CSSのサブセットをサポート）                       │\n└─────────────────────────────────────────────────────────┘\n                          ↓\n┌─────────────────────────────────────────────────────────┐\n│  Resvg                                                   │\n│  SVG → PNG に変換（ラスタライズ）                        │\n│  （Rustベースで高速）                                    │\n└─────────────────────────────────────────────────────────┘\n                          ↓\n┌─────────────────────────────────────────────────────────┐\n│  PNG画像をレスポンス                                     │\n│  Content-Type: image/png                                 │\n└─────────────────────────────────────────────────────────┘\n","text",[148,149,145],"code",{"__ignoreMap":150},"",[30,152],{},[15,154,156],{"id":155},"qa","Q&A",[115,158,160],{"id":159},"q1-satoriとは何かなぜ選ばれるのか","Q1: Satoriとは何か？なぜ選ばれるのか？",[19,162,163,166],{},[25,164,165],{},"A",": Vercelが開発したライブラリであるSatoriは、JSX/HTML+CSSをSVGに変換する。",[19,168,169,172],{},[25,170,171],{},"選ばれる理由",":",[120,174,175,178,181,184],{},[123,176,177],{},"React/JSXライクな構文でデザインを記述できる",[123,179,180],{},"Flexboxレイアウトをサポート",[123,182,183],{},"エッジランタイム（Cloudflare Workers等）で動作",[123,185,186,189],{},[148,187,188],{},"@vercel/og","の内部で使われている実績",[19,191,192,172],{},[25,193,194],{},"代替手段",[37,196,197,207],{},[40,198,199],{},[43,200,201,204],{},[46,202,203],{},"ライブラリ",[46,205,206],{},"特徴",[56,208,209,217,225,233],{},[43,210,211,214],{},[61,212,213],{},"Puppeteer/Playwright",[61,215,216],{},"ブラウザでレンダリング。重い。エッジ不可",[43,218,219,222],{},[61,220,221],{},"node-canvas",[61,223,224],{},"Canvasで描画。Node.js依存",[43,226,227,230],{},[61,228,229],{},"Sharp",[61,231,232],{},"画像処理特化。テキスト描画は弱い",[43,234,235,238],{},[61,236,237],{},"Satori",[61,239,240],{},"軽量、エッジ対応、JSX構文",[30,242],{},[115,244,246],{"id":245},"q2-日本語フォントの埋め込みはどうやるのか","Q2: 日本語フォントの埋め込みはどうやるのか？",[19,248,249,251],{},[25,250,165],{},": Satoriはフォントファイルを直接読み込んで埋め込む。",[141,253,257],{"className":254,"code":255,"language":256,"meta":150,"style":150},"language-typescript shiki shiki-themes vitesse-light vitesse-light","import satori from 'satori'\n\n// フォントファイルを読み込み（ArrayBuffer形式）\nconst fontData = await fetch('https://example.com/NotoSansJP-Bold.ttf')\n  .then(res => res.arrayBuffer())\n\nconst svg = await satori(\n  \u003Cdiv style={{ fontFamily: 'Noto Sans JP' }}>こんにちは\u003C/div>,\n  {\n    width: 1200,\n    height: 630,\n    fonts: [\n      {\n        name: 'Noto Sans JP',\n        data: fontData,\n        weight: 700,\n        style: 'normal',\n      },\n    ],\n  }\n)\n","typescript",[148,258,259,286,293,300,335,364,369,386,431,437,453,466,475,481,497,509,522,539,545,551,557],{"__ignoreMap":150},[260,261,264,268,272,275,279,283],"span",{"class":262,"line":263},"line",1,[260,265,267],{"class":266},"sHkkW","import",[260,269,271],{"class":270},"s4oTP"," satori",[260,273,274],{"class":266}," from",[260,276,278],{"class":277},"sMJiu"," '",[260,280,282],{"class":281},"sdGka","satori",[260,284,285],{"class":277},"'\n",[260,287,289],{"class":262,"line":288},2,[260,290,292],{"emptyLinePlaceholder":291},true,"\n",[260,294,296],{"class":262,"line":295},3,[260,297,299],{"class":298},"sxvE3","// フォントファイルを読み込み（ArrayBuffer形式）\n",[260,301,303,307,310,314,317,321,324,327,330,332],{"class":262,"line":302},4,[260,304,306],{"class":305},"stQ0i","const ",[260,308,309],{"class":270},"fontData",[260,311,313],{"class":312},"shFtX"," =",[260,315,316],{"class":266}," await",[260,318,320],{"class":319},"senZ8"," fetch",[260,322,323],{"class":312},"(",[260,325,326],{"class":277},"'",[260,328,329],{"class":281},"https://example.com/NotoSansJP-Bold.ttf",[260,331,326],{"class":277},[260,333,334],{"class":312},")\n",[260,336,338,341,344,346,349,352,355,358,361],{"class":262,"line":337},5,[260,339,340],{"class":312},"  .",[260,342,343],{"class":319},"then",[260,345,323],{"class":312},[260,347,348],{"class":270},"res",[260,350,351],{"class":312}," =>",[260,353,354],{"class":270}," res",[260,356,357],{"class":312},".",[260,359,360],{"class":319},"arrayBuffer",[260,362,363],{"class":312},"())\n",[260,365,367],{"class":262,"line":366},6,[260,368,292],{"emptyLinePlaceholder":291},[260,370,372,374,377,379,381,383],{"class":262,"line":371},7,[260,373,306],{"class":305},[260,375,376],{"class":270},"svg",[260,378,313],{"class":312},[260,380,316],{"class":266},[260,382,271],{"class":319},[260,384,385],{"class":312},"(\n",[260,387,389,392,395,398,401,404,407,409,412,414,417,420,423,426,428],{"class":262,"line":388},8,[260,390,391],{"class":312},"  \u003C",[260,393,394],{"class":270},"div",[260,396,397],{"class":270}," style",[260,399,400],{"class":312},"={{ ",[260,402,403],{"class":319},"fontFamily",[260,405,406],{"class":312},": ",[260,408,326],{"class":277},[260,410,411],{"class":281},"Noto Sans JP",[260,413,326],{"class":277},[260,415,416],{"class":312}," }}>",[260,418,419],{"class":270},"こんにちは",[260,421,422],{"class":312},"\u003C",[260,424,425],{"class":305},"/",[260,427,394],{"class":270},[260,429,430],{"class":312},">,\n",[260,432,434],{"class":262,"line":433},9,[260,435,436],{"class":312},"  {\n",[260,438,440,444,446,450],{"class":262,"line":439},10,[260,441,443],{"class":442},"sz8Xr","    width",[260,445,406],{"class":312},[260,447,449],{"class":448},"sM54T","1200",[260,451,452],{"class":312},",\n",[260,454,456,459,461,464],{"class":262,"line":455},11,[260,457,458],{"class":442},"    height",[260,460,406],{"class":312},[260,462,463],{"class":448},"630",[260,465,452],{"class":312},[260,467,469,472],{"class":262,"line":468},12,[260,470,471],{"class":442},"    fonts",[260,473,474],{"class":312},": [\n",[260,476,478],{"class":262,"line":477},13,[260,479,480],{"class":312},"      {\n",[260,482,484,487,489,491,493,495],{"class":262,"line":483},14,[260,485,486],{"class":442},"        name",[260,488,406],{"class":312},[260,490,326],{"class":277},[260,492,411],{"class":281},[260,494,326],{"class":277},[260,496,452],{"class":312},[260,498,500,503,505,507],{"class":262,"line":499},15,[260,501,502],{"class":442},"        data",[260,504,406],{"class":312},[260,506,309],{"class":270},[260,508,452],{"class":312},[260,510,512,515,517,520],{"class":262,"line":511},16,[260,513,514],{"class":442},"        weight",[260,516,406],{"class":312},[260,518,519],{"class":448},"700",[260,521,452],{"class":312},[260,523,525,528,530,532,535,537],{"class":262,"line":524},17,[260,526,527],{"class":442},"        style",[260,529,406],{"class":312},[260,531,326],{"class":277},[260,533,534],{"class":281},"normal",[260,536,326],{"class":277},[260,538,452],{"class":312},[260,540,542],{"class":262,"line":541},18,[260,543,544],{"class":312},"      },\n",[260,546,548],{"class":262,"line":547},19,[260,549,550],{"class":312},"    ],\n",[260,552,554],{"class":262,"line":553},20,[260,555,556],{"class":312},"  }\n",[260,558,560],{"class":262,"line":559},21,[260,561,334],{"class":312},[19,563,564,172],{},[25,565,566],{},"注意点",[120,568,569,572,575],{},[123,570,571],{},"日本語フォントは大きい（Noto Sans JP Regular: 約5MB）",[123,573,574],{},"サブセット化で軽量化可能（後述）",[123,576,577],{},"Cloudflare Workersのメモリ制限に注意",[30,579],{},[115,581,583],{"id":582},"q3-フォントのサブセット化とは","Q3: フォントのサブセット化とは？",[19,585,586,588],{},[25,587,165],{},": 使用する文字だけを抽出してフォントファイルを軽量化する技術。",[141,590,593],{"className":591,"code":592,"language":146},[144],"Noto Sans JP Full:    約5MB（全文字）\nサブセット化後:       約100KB〜500KB（必要な文字のみ）\n",[148,594,592],{"__ignoreMap":150},[19,596,597,172],{},[25,598,599],{},"サブセット化ツール",[120,601,602,611,618],{},[123,603,604],{},[605,606,610],"a",{"href":607,"rel":608},"https://github.com/fonttools/fonttools",[609],"nofollow","fonttools (pyftsubset)",[123,612,613],{},[605,614,617],{"href":615,"rel":616},"https://github.com/zachleat/glyphhanger",[609],"glyphhanger",[123,619,620],{},[605,621,624],{"href":622,"rel":623},"https://transfonter.org/",[609],"Transfonter",[19,626,627,172],{},[25,628,629],{},"クイズ結果の場合",[141,631,634],{"className":632,"code":633,"language":146},[144],"必要な文字: 0123456789問正解/修飾語の順序句読点助詞使い方クイズ\n→ 数十文字程度なので大幅に軽量化可能\n",[148,635,633],{"__ignoreMap":150},[30,637],{},[115,639,641],{"id":640},"q4-resvgとはなぜsvgpngの変換が必要","Q4: Resvgとは？なぜSVG→PNGの変換が必要？",[19,643,644,646],{},[25,645,165],{},": Rustで書かれたSVGレンダラーであるResvgは、SVGをPNG等のラスター画像に変換する。",[19,648,649,172],{},[25,650,651],{},"なぜ変換が必要か",[120,653,654,657,660],{},[123,655,656],{},"SNS（Twitter/X、Facebook等）はOGP画像としてSVGをサポートしていない",[123,658,659],{},"PNG/JPEGのみ対応",[123,661,662],{},"SatoriはSVGまでしか生成しないため、PNG変換が必要",[141,664,666],{"className":254,"code":665,"language":256,"meta":150,"style":150},"import { Resvg } from '@resvg/resvg-js'\n\nconst svg = await satori(/* ... */)\n\nconst resvg = new Resvg(svg, {\n  fitTo: {\n    mode: 'width',\n    value: 1200,\n  },\n})\nconst pngBuffer = resvg.render().asPng()\n",[148,667,668,690,694,713,717,742,750,766,777,782,787],{"__ignoreMap":150},[260,669,670,672,675,678,681,683,685,688],{"class":262,"line":263},[260,671,267],{"class":266},[260,673,674],{"class":312}," {",[260,676,677],{"class":270}," Resvg",[260,679,680],{"class":312}," }",[260,682,274],{"class":266},[260,684,278],{"class":277},[260,686,687],{"class":281},"@resvg/resvg-js",[260,689,285],{"class":277},[260,691,692],{"class":262,"line":288},[260,693,292],{"emptyLinePlaceholder":291},[260,695,696,698,700,702,704,706,708,711],{"class":262,"line":295},[260,697,306],{"class":305},[260,699,376],{"class":270},[260,701,313],{"class":312},[260,703,316],{"class":266},[260,705,271],{"class":319},[260,707,323],{"class":312},[260,709,710],{"class":298},"/* ... */",[260,712,334],{"class":312},[260,714,715],{"class":262,"line":302},[260,716,292],{"emptyLinePlaceholder":291},[260,718,719,721,724,726,729,732,734,736,739],{"class":262,"line":337},[260,720,306],{"class":305},[260,722,723],{"class":270},"resvg",[260,725,313],{"class":312},[260,727,728],{"class":305}," new ",[260,730,731],{"class":319},"Resvg",[260,733,323],{"class":312},[260,735,376],{"class":270},[260,737,738],{"class":312},",",[260,740,741],{"class":312}," {\n",[260,743,744,747],{"class":262,"line":366},[260,745,746],{"class":442},"  fitTo",[260,748,749],{"class":312},": {\n",[260,751,752,755,757,759,762,764],{"class":262,"line":371},[260,753,754],{"class":442},"    mode",[260,756,406],{"class":312},[260,758,326],{"class":277},[260,760,761],{"class":281},"width",[260,763,326],{"class":277},[260,765,452],{"class":312},[260,767,768,771,773,775],{"class":262,"line":388},[260,769,770],{"class":442},"    value",[260,772,406],{"class":312},[260,774,449],{"class":448},[260,776,452],{"class":312},[260,778,779],{"class":262,"line":433},[260,780,781],{"class":312},"  },\n",[260,783,784],{"class":262,"line":439},[260,785,786],{"class":312},"})\n",[260,788,789,791,794,796,799,801,804,807,810],{"class":262,"line":455},[260,790,306],{"class":305},[260,792,793],{"class":270},"pngBuffer",[260,795,313],{"class":312},[260,797,798],{"class":270}," resvg",[260,800,357],{"class":312},[260,802,803],{"class":319},"render",[260,805,806],{"class":312},"().",[260,808,809],{"class":319},"asPng",[260,811,812],{"class":312},"()\n",[30,814],{},[115,816,818],{"id":817},"q5-satoriだけでは動的ogpは完成しないのか","Q5: Satoriだけでは動的OGPは完成しないのか？",[19,820,821,823],{},[25,822,165],{},": いいえ、Satoriだけでは完成しない。Resvgが必須だ。",[141,825,828],{"className":826,"code":827,"language":146},[144],"処理フロー:\n┌─────────┐      ┌─────────┐      ┌─────────┐\n│ Satori  │ ──→  │   SVG   │ ──→  │  Resvg  │ ──→  PNG\n└─────────┘      └─────────┘      └─────────┘\n   JSX/CSS        中間形式          ラスタライズ\n",[148,829,827],{"__ignoreMap":150},[19,831,832,172],{},[25,833,834],{},"Satoriの役割",[120,836,837,840],{},[123,838,839],{},"JSX/HTML+CSSをSVGに変換",[123,841,842],{},"SVGはベクター形式（テキストデータ）",[19,844,845,172],{},[25,846,847],{},"Resvgが必要な理由",[120,849,850,854,857],{},[123,851,852],{},[25,853,656],{},[123,855,856],{},"PNG/JPEGのラスター形式のみ対応",[123,858,859],{},"SatoriはSVGまでしか生成できないため、Resvgでラスタライズ（PNG変換）が必須",[19,861,862,172],{},[25,863,864],{},"よくある誤解",[866,867,868],"blockquote",{},[19,869,870],{},"「Satoriで画像が生成されるならそのまま使えるのでは？」",[19,872,873],{},"→ SatoriはSVG（XMLテキスト）を生成するだけ。ブラウザでは表示できますが、SNSのOGP画像には使えません。",[19,875,876,172],{},[25,877,878],{},"必要なパッケージ",[141,880,884],{"className":881,"code":882,"language":883,"meta":150,"style":150},"language-json shiki shiki-themes vitesse-light vitesse-light","{\n  \"dependencies\": {\n    \"satori\": \"^0.18.3\",           // JSX → SVG\n    \"@resvg/resvg-js\": \"^2.6.2\"    // SVG → PNG\n  }\n}\n","json",[148,885,886,891,907,931,951,955],{"__ignoreMap":150},[260,887,888],{"class":262,"line":263},[260,889,890],{"class":312},"{\n",[260,892,893,897,900,903,905],{"class":262,"line":288},[260,894,896],{"class":895},"sqvqQ","  \"",[260,898,899],{"class":442},"dependencies",[260,901,902],{"class":895},"\"",[260,904,172],{"class":312},[260,906,741],{"class":312},[260,908,909,912,914,916,918,921,924,926,928],{"class":262,"line":295},[260,910,911],{"class":895},"    \"",[260,913,282],{"class":442},[260,915,902],{"class":895},[260,917,172],{"class":312},[260,919,920],{"class":277}," \"",[260,922,923],{"class":281},"^0.18.3",[260,925,902],{"class":277},[260,927,738],{"class":312},[260,929,930],{"class":298},"           // JSX → SVG\n",[260,932,933,935,937,939,941,943,946,948],{"class":262,"line":302},[260,934,911],{"class":895},[260,936,687],{"class":442},[260,938,902],{"class":895},[260,940,172],{"class":312},[260,942,920],{"class":277},[260,944,945],{"class":281},"^2.6.2",[260,947,902],{"class":277},[260,949,950],{"class":298},"    // SVG → PNG\n",[260,952,953],{"class":262,"line":337},[260,954,556],{"class":312},[260,956,957],{"class":262,"line":366},[260,958,959],{"class":312},"}\n",[30,961],{},[115,963,965],{"id":964},"q6-cloudflare-workersの制限は","Q6: Cloudflare Workersの制限は？",[19,967,968,970],{},[25,969,165],{},": Cloudflare Workersにはリソース制限がある。",[37,972,973,985],{},[40,974,975],{},[43,976,977,979,982],{},[46,978,48],{},[46,980,981],{},"Free",[46,983,984],{},"Paid",[56,986,987,998,1008],{},[43,988,989,992,995],{},[61,990,991],{},"CPU時間",[61,993,994],{},"10ms",[61,996,997],{},"50ms（Unbound: 30s）",[43,999,1000,1003,1006],{},[61,1001,1002],{},"メモリ",[61,1004,1005],{},"128MB",[61,1007,1005],{},[43,1009,1010,1013,1016],{},[61,1011,1012],{},"スクリプトサイズ",[61,1014,1015],{},"1MB",[61,1017,1018],{},"10MB",[19,1020,1021,172],{},[25,1022,1023],{},"OGP生成での注意点",[120,1025,1026,1029,1032],{},[123,1027,1028],{},"フォントファイルが大きいとスクリプトサイズ制限に抵触",[123,1030,1031],{},"複雑なSVG生成はCPU時間を消費",[123,1033,1034],{},"Resvgのラスタライズも負荷がかかる",[19,1036,1037,172],{},[25,1038,1039],{},"対策",[120,1041,1042,1045,1048],{},[123,1043,1044],{},"フォントをサブセット化",[123,1046,1047],{},"フォントを外部（R2、CDN）から取得",[123,1049,1050],{},"生成結果をキャッシュ",[30,1052],{},[115,1054,1056],{"id":1055},"q7-キャッシュ戦略は","Q7: キャッシュ戦略は？",[19,1058,1059,1061],{},[25,1060,165],{},": 同じパラメータなら同じ画像が生成されるため積極的にキャッシュすべきだ。",[141,1063,1065],{"className":254,"code":1064,"language":256,"meta":150,"style":150},"// Cloudflare Workersでのキャッシュ例\nexport default {\n  async fetch(request: Request, env: Env, ctx: ExecutionContext) {\n    const cache = caches.default\n    const cacheKey = new Request(request.url, request)\n\n    // キャッシュチェック\n    let response = await cache.match(cacheKey)\n    if (response) {\n      return response\n    }\n\n    // OGP画像生成\n    const png = await generateOgImage(request)\n\n    response = new Response(png, {\n      headers: {\n        'Content-Type': 'image/png',\n        'Cache-Control': 'public, max-age=86400', // 24時間\n      },\n    })\n\n    // キャッシュに保存\n    ctx.waitUntil(cache.put(cacheKey, response.clone()))\n\n    return response\n  },\n}\n",[148,1066,1067,1072,1082,1124,1142,1171,1175,1180,1206,1218,1226,1231,1235,1240,1260,1264,1285,1292,1313,1336,1340,1345,1350,1356,1392,1397,1405,1410],{"__ignoreMap":150},[260,1068,1069],{"class":262,"line":263},[260,1070,1071],{"class":298},"// Cloudflare Workersでのキャッシュ例\n",[260,1073,1074,1077,1080],{"class":262,"line":288},[260,1075,1076],{"class":266},"export",[260,1078,1079],{"class":266}," default",[260,1081,741],{"class":312},[260,1083,1084,1087,1089,1091,1094,1096,1100,1103,1106,1108,1111,1113,1116,1118,1121],{"class":262,"line":295},[260,1085,1086],{"class":305},"  async",[260,1088,320],{"class":319},[260,1090,323],{"class":312},[260,1092,1093],{"class":270},"request",[260,1095,406],{"class":312},[260,1097,1099],{"class":1098},"sSkh3","Request",[260,1101,1102],{"class":312},", ",[260,1104,1105],{"class":270},"env",[260,1107,406],{"class":312},[260,1109,1110],{"class":1098},"Env",[260,1112,1102],{"class":312},[260,1114,1115],{"class":270},"ctx",[260,1117,406],{"class":312},[260,1119,1120],{"class":1098},"ExecutionContext",[260,1122,1123],{"class":312},") {\n",[260,1125,1126,1129,1132,1134,1137,1139],{"class":262,"line":302},[260,1127,1128],{"class":305},"    const ",[260,1130,1131],{"class":270},"cache",[260,1133,313],{"class":312},[260,1135,1136],{"class":270}," caches",[260,1138,357],{"class":312},[260,1140,1141],{"class":270},"default\n",[260,1143,1144,1146,1149,1151,1153,1155,1157,1159,1161,1164,1166,1169],{"class":262,"line":337},[260,1145,1128],{"class":305},[260,1147,1148],{"class":270},"cacheKey",[260,1150,313],{"class":312},[260,1152,728],{"class":305},[260,1154,1099],{"class":319},[260,1156,323],{"class":312},[260,1158,1093],{"class":270},[260,1160,357],{"class":312},[260,1162,1163],{"class":270},"url",[260,1165,738],{"class":312},[260,1167,1168],{"class":270}," request",[260,1170,334],{"class":312},[260,1172,1173],{"class":262,"line":366},[260,1174,292],{"emptyLinePlaceholder":291},[260,1176,1177],{"class":262,"line":371},[260,1178,1179],{"class":298},"    // キャッシュチェック\n",[260,1181,1182,1185,1188,1190,1192,1195,1197,1200,1202,1204],{"class":262,"line":388},[260,1183,1184],{"class":305},"    let ",[260,1186,1187],{"class":270},"response",[260,1189,313],{"class":312},[260,1191,316],{"class":266},[260,1193,1194],{"class":270}," cache",[260,1196,357],{"class":312},[260,1198,1199],{"class":319},"match",[260,1201,323],{"class":312},[260,1203,1148],{"class":270},[260,1205,334],{"class":312},[260,1207,1208,1211,1214,1216],{"class":262,"line":433},[260,1209,1210],{"class":266},"    if",[260,1212,1213],{"class":312}," (",[260,1215,1187],{"class":270},[260,1217,1123],{"class":312},[260,1219,1220,1223],{"class":262,"line":439},[260,1221,1222],{"class":266},"      return",[260,1224,1225],{"class":270}," response\n",[260,1227,1228],{"class":262,"line":455},[260,1229,1230],{"class":312},"    }\n",[260,1232,1233],{"class":262,"line":468},[260,1234,292],{"emptyLinePlaceholder":291},[260,1236,1237],{"class":262,"line":477},[260,1238,1239],{"class":298},"    // OGP画像生成\n",[260,1241,1242,1244,1247,1249,1251,1254,1256,1258],{"class":262,"line":483},[260,1243,1128],{"class":305},[260,1245,1246],{"class":270},"png",[260,1248,313],{"class":312},[260,1250,316],{"class":266},[260,1252,1253],{"class":319}," generateOgImage",[260,1255,323],{"class":312},[260,1257,1093],{"class":270},[260,1259,334],{"class":312},[260,1261,1262],{"class":262,"line":499},[260,1263,292],{"emptyLinePlaceholder":291},[260,1265,1266,1269,1272,1275,1278,1280,1282],{"class":262,"line":511},[260,1267,1268],{"class":270},"    response",[260,1270,1271],{"class":312}," = ",[260,1273,1274],{"class":305},"new",[260,1276,1277],{"class":319}," Response",[260,1279,323],{"class":312},[260,1281,1246],{"class":270},[260,1283,1284],{"class":312},", {\n",[260,1286,1287,1290],{"class":262,"line":524},[260,1288,1289],{"class":442},"      headers",[260,1291,749],{"class":312},[260,1293,1294,1297,1300,1302,1304,1306,1309,1311],{"class":262,"line":541},[260,1295,1296],{"class":277},"        '",[260,1298,1299],{"class":281},"Content-Type",[260,1301,326],{"class":277},[260,1303,406],{"class":312},[260,1305,326],{"class":277},[260,1307,1308],{"class":281},"image/png",[260,1310,326],{"class":277},[260,1312,452],{"class":312},[260,1314,1315,1317,1320,1322,1324,1326,1329,1331,1333],{"class":262,"line":547},[260,1316,1296],{"class":277},[260,1318,1319],{"class":281},"Cache-Control",[260,1321,326],{"class":277},[260,1323,406],{"class":312},[260,1325,326],{"class":277},[260,1327,1328],{"class":281},"public, max-age=86400",[260,1330,326],{"class":277},[260,1332,1102],{"class":312},[260,1334,1335],{"class":298},"// 24時間\n",[260,1337,1338],{"class":262,"line":553},[260,1339,544],{"class":312},[260,1341,1342],{"class":262,"line":559},[260,1343,1344],{"class":312},"    })\n",[260,1346,1348],{"class":262,"line":1347},22,[260,1349,292],{"emptyLinePlaceholder":291},[260,1351,1353],{"class":262,"line":1352},23,[260,1354,1355],{"class":298},"    // キャッシュに保存\n",[260,1357,1359,1362,1364,1367,1369,1371,1373,1376,1378,1380,1382,1384,1386,1389],{"class":262,"line":1358},24,[260,1360,1361],{"class":270},"    ctx",[260,1363,357],{"class":312},[260,1365,1366],{"class":319},"waitUntil",[260,1368,323],{"class":312},[260,1370,1131],{"class":270},[260,1372,357],{"class":312},[260,1374,1375],{"class":319},"put",[260,1377,323],{"class":312},[260,1379,1148],{"class":270},[260,1381,1102],{"class":312},[260,1383,1187],{"class":270},[260,1385,357],{"class":312},[260,1387,1388],{"class":319},"clone",[260,1390,1391],{"class":312},"()))\n",[260,1393,1395],{"class":262,"line":1394},25,[260,1396,292],{"emptyLinePlaceholder":291},[260,1398,1400,1403],{"class":262,"line":1399},26,[260,1401,1402],{"class":266},"    return",[260,1404,1225],{"class":270},[260,1406,1408],{"class":262,"line":1407},27,[260,1409,781],{"class":312},[260,1411,1413],{"class":262,"line":1412},28,[260,1414,959],{"class":312},[19,1416,1417,172],{},[25,1418,1419],{},"キャッシュキーの設計",[141,1421,1424],{"className":1422,"code":1423,"language":146},[144],"/api/og?category=modification-order&correct=8&total=20\n         ↓\nキャッシュキー: og:modification-order:8:20\n",[148,1425,1423],{"__ignoreMap":150},[30,1427],{},[115,1429,1431],{"id":1430},"q8-nuxtで動的にogpメタタグを設定する方法は","Q8: Nuxtで動的にOGPメタタグを設定する方法は？",[19,1433,1434,406,1436,1439,1440,1443],{},[25,1435,165],{},[148,1437,1438],{},"useHead","または",[148,1441,1442],{},"useSeoMeta","を使用する。",[141,1445,1449],{"className":1446,"code":1447,"language":1448,"meta":150,"style":150},"language-vue shiki shiki-themes vitesse-light vitesse-light","\u003Cscript setup lang=\"ts\">\nconst route = useRoute()\nconst { category, correct, total } = route.query\n\nuseSeoMeta({\n  ogImage: `https://eurekapu.com/api/og/japanese-quiz?category=${category}&correct=${correct}&total=${total}`,\n  ogTitle: `${getCategoryName(category)}クイズ - ${correct}/${total}問正解！`,\n  ogDescription: '日本語作文クイズに挑戦しよう',\n  twitterCard: 'summary_large_image',\n})\n\u003C/script>\n","vue",[148,1450,1451,1477,1491,1523,1527,1534,1580,1627,1643,1659,1663],{"__ignoreMap":150},[260,1452,1453,1455,1458,1461,1464,1467,1469,1472,1474],{"class":262,"line":263},[260,1454,422],{"class":312},[260,1456,1457],{"class":266},"script",[260,1459,1460],{"class":270}," setup",[260,1462,1463],{"class":270}," lang",[260,1465,1466],{"class":312},"=",[260,1468,902],{"class":277},[260,1470,1471],{"class":281},"ts",[260,1473,902],{"class":277},[260,1475,1476],{"class":312},">\n",[260,1478,1479,1481,1484,1486,1489],{"class":262,"line":288},[260,1480,306],{"class":305},[260,1482,1483],{"class":270},"route",[260,1485,313],{"class":312},[260,1487,1488],{"class":319}," useRoute",[260,1490,812],{"class":312},[260,1492,1493,1495,1498,1501,1503,1506,1508,1511,1513,1515,1518,1520],{"class":262,"line":295},[260,1494,306],{"class":305},[260,1496,1497],{"class":312},"{",[260,1499,1500],{"class":270}," category",[260,1502,738],{"class":312},[260,1504,1505],{"class":270}," correct",[260,1507,738],{"class":312},[260,1509,1510],{"class":270}," total",[260,1512,680],{"class":312},[260,1514,313],{"class":312},[260,1516,1517],{"class":270}," route",[260,1519,357],{"class":312},[260,1521,1522],{"class":270},"query\n",[260,1524,1525],{"class":262,"line":302},[260,1526,292],{"emptyLinePlaceholder":291},[260,1528,1529,1531],{"class":262,"line":337},[260,1530,1442],{"class":319},[260,1532,1533],{"class":312},"({\n",[260,1535,1536,1539,1541,1544,1547,1550,1553,1556,1559,1561,1564,1566,1569,1571,1574,1576,1578],{"class":262,"line":366},[260,1537,1538],{"class":442},"  ogImage",[260,1540,406],{"class":312},[260,1542,1543],{"class":277},"`",[260,1545,1546],{"class":281},"https://eurekapu.com/api/og/japanese-quiz?category=",[260,1548,1549],{"class":266},"${",[260,1551,1552],{"class":281},"category",[260,1554,1555],{"class":266},"}",[260,1557,1558],{"class":281},"&correct=",[260,1560,1549],{"class":266},[260,1562,1563],{"class":281},"correct",[260,1565,1555],{"class":266},[260,1567,1568],{"class":281},"&total=",[260,1570,1549],{"class":266},[260,1572,1573],{"class":281},"total",[260,1575,1555],{"class":266},[260,1577,1543],{"class":277},[260,1579,452],{"class":312},[260,1581,1582,1585,1587,1589,1591,1594,1596,1598,1601,1603,1606,1608,1610,1612,1614,1616,1618,1620,1623,1625],{"class":262,"line":371},[260,1583,1584],{"class":442},"  ogTitle",[260,1586,406],{"class":312},[260,1588,1543],{"class":277},[260,1590,1549],{"class":266},[260,1592,1593],{"class":319},"getCategoryName",[260,1595,323],{"class":312},[260,1597,1552],{"class":281},[260,1599,1600],{"class":312},")",[260,1602,1555],{"class":266},[260,1604,1605],{"class":281},"クイズ - ",[260,1607,1549],{"class":266},[260,1609,1563],{"class":281},[260,1611,1555],{"class":266},[260,1613,425],{"class":281},[260,1615,1549],{"class":266},[260,1617,1573],{"class":281},[260,1619,1555],{"class":266},[260,1621,1622],{"class":281},"問正解！",[260,1624,1543],{"class":277},[260,1626,452],{"class":312},[260,1628,1629,1632,1634,1636,1639,1641],{"class":262,"line":388},[260,1630,1631],{"class":442},"  ogDescription",[260,1633,406],{"class":312},[260,1635,326],{"class":277},[260,1637,1638],{"class":281},"日本語作文クイズに挑戦しよう",[260,1640,326],{"class":277},[260,1642,452],{"class":312},[260,1644,1645,1648,1650,1652,1655,1657],{"class":262,"line":433},[260,1646,1647],{"class":442},"  twitterCard",[260,1649,406],{"class":312},[260,1651,326],{"class":277},[260,1653,1654],{"class":281},"summary_large_image",[260,1656,326],{"class":277},[260,1658,452],{"class":312},[260,1660,1661],{"class":262,"line":439},[260,1662,786],{"class":312},[260,1664,1665,1668,1670],{"class":262,"line":455},[260,1666,1667],{"class":312},"\u003C/",[260,1669,1457],{"class":266},[260,1671,1476],{"class":312},[19,1673,1674,1677],{},[25,1675,1676],{},"注意",": SSRが必要。クライアントサイドでメタタグを変更してもクローラーには見えない。",[30,1679],{},[115,1681,1683],{"id":1682},"q9-twitterxカードの検証デバッグ方法は","Q9: Twitter/Xカードの検証・デバッグ方法は？",[19,1685,1686,1688],{},[25,1687,165],{},": 以下のツールを使用する。",[37,1690,1691,1701],{},[40,1692,1693],{},[43,1694,1695,1698],{},[46,1696,1697],{},"ツール",[46,1699,1700],{},"URL",[56,1702,1703,1714,1725],{},[43,1704,1705,1708],{},[61,1706,1707],{},"Twitter Card Validator",[61,1709,1710],{},[605,1711,1712],{"href":1712,"rel":1713},"https://cards-dev.twitter.com/validator",[609],[43,1715,1716,1719],{},[61,1717,1718],{},"Facebook Sharing Debugger",[61,1720,1721],{},[605,1722,1723],{"href":1723,"rel":1724},"https://developers.facebook.com/tools/debug/",[609],[43,1726,1727,1730],{},[61,1728,1729],{},"LinkedIn Post Inspector",[61,1731,1732],{},[605,1733,1734],{"href":1734,"rel":1735},"https://www.linkedin.com/post-inspector/",[609],[19,1737,1738,172],{},[25,1739,1740],{},"デバッグ手順",[1742,1743,1744,1747,1750],"ol",{},[123,1745,1746],{},"OGP画像URLに直接アクセスして画像が表示されるか確認",[123,1748,1749],{},"各SNSのデバッガーでメタタグが正しく認識されるか確認",[123,1751,1752],{},"キャッシュが古い場合は「Scrape Again」で更新",[19,1754,1755,172],{},[25,1756,1757],{},"よくある問題",[120,1759,1760,1763,1766],{},[123,1761,1762],{},"画像が表示されない → URLが公開アクセス可能か確認",[123,1764,1765],{},"古い画像が表示される → SNS側のキャッシュ。デバッガーで更新",[123,1767,1768],{},"サイズが合わない → 1200x630pxを推奨",[30,1770],{},[115,1772,1774],{"id":1773},"q10-vercelogとの違いは","Q10: @vercel/ogとの違いは？",[19,1776,1777,406,1779,1781],{},[25,1778,165],{},[148,1780,188],{},"はVercelが提供するOGP画像生成のオールインワンパッケージだ。",[141,1783,1785],{"className":254,"code":1784,"language":256,"meta":150,"style":150},"// @vercel/og（Vercel専用）\nimport { ImageResponse } from '@vercel/og'\n\nexport const config = { runtime: 'edge' }\n\nexport default function handler() {\n  return new ImageResponse(\n    \u003Cdiv>Hello World\u003C/div>,\n    { width: 1200, height: 630 }\n  )\n}\n",[148,1786,1787,1792,1811,1815,1845,1849,1866,1878,1902,1924,1929],{"__ignoreMap":150},[260,1788,1789],{"class":262,"line":263},[260,1790,1791],{"class":298},"// @vercel/og（Vercel専用）\n",[260,1793,1794,1796,1798,1801,1803,1805,1807,1809],{"class":262,"line":288},[260,1795,267],{"class":266},[260,1797,674],{"class":312},[260,1799,1800],{"class":270}," ImageResponse",[260,1802,680],{"class":312},[260,1804,274],{"class":266},[260,1806,278],{"class":277},[260,1808,188],{"class":281},[260,1810,285],{"class":277},[260,1812,1813],{"class":262,"line":295},[260,1814,292],{"emptyLinePlaceholder":291},[260,1816,1817,1819,1822,1825,1827,1830,1833,1835,1837,1840,1842],{"class":262,"line":302},[260,1818,1076],{"class":266},[260,1820,1821],{"class":305}," const ",[260,1823,1824],{"class":270},"config",[260,1826,313],{"class":312},[260,1828,1829],{"class":312}," { ",[260,1831,1832],{"class":442},"runtime",[260,1834,406],{"class":312},[260,1836,326],{"class":277},[260,1838,1839],{"class":281},"edge",[260,1841,326],{"class":277},[260,1843,1844],{"class":312}," }\n",[260,1846,1847],{"class":262,"line":337},[260,1848,292],{"emptyLinePlaceholder":291},[260,1850,1851,1853,1855,1858,1861,1864],{"class":262,"line":366},[260,1852,1076],{"class":266},[260,1854,1079],{"class":266},[260,1856,1857],{"class":305}," function",[260,1859,1860],{"class":319}," handler",[260,1862,1863],{"class":312},"()",[260,1865,741],{"class":312},[260,1867,1868,1871,1874,1876],{"class":262,"line":371},[260,1869,1870],{"class":266},"  return",[260,1872,1873],{"class":305}," new",[260,1875,1800],{"class":319},[260,1877,385],{"class":312},[260,1879,1880,1883,1885,1888,1891,1894,1896,1898,1900],{"class":262,"line":388},[260,1881,1882],{"class":312},"    \u003C",[260,1884,394],{"class":1098},[260,1886,1887],{"class":312},">",[260,1889,1890],{"class":270},"Hello",[260,1892,1893],{"class":270}," World",[260,1895,422],{"class":312},[260,1897,425],{"class":305},[260,1899,394],{"class":270},[260,1901,430],{"class":312},[260,1903,1904,1907,1909,1911,1913,1915,1918,1920,1922],{"class":262,"line":433},[260,1905,1906],{"class":312},"    { ",[260,1908,761],{"class":442},[260,1910,406],{"class":312},[260,1912,449],{"class":448},[260,1914,1102],{"class":312},[260,1916,1917],{"class":442},"height",[260,1919,406],{"class":312},[260,1921,463],{"class":448},[260,1923,1844],{"class":312},[260,1925,1926],{"class":262,"line":439},[260,1927,1928],{"class":312},"  )\n",[260,1930,1931],{"class":262,"line":455},[260,1932,959],{"class":312},[19,1934,1935,172],{},[25,1936,1937],{},"比較",[37,1939,1940,1951],{},[40,1941,1942],{},[43,1943,1944,1946,1948],{},[46,1945,48],{},[46,1947,188],{},[46,1949,1950],{},"Satori + Resvg",[56,1952,1953,1964,1975,1986],{},[43,1954,1955,1958,1961],{},[61,1956,1957],{},"対象環境",[61,1959,1960],{},"Vercel Edge Functions",[61,1962,1963],{},"どこでも",[43,1965,1966,1969,1972],{},[61,1967,1968],{},"セットアップ",[61,1970,1971],{},"簡単（オールインワン）",[61,1973,1974],{},"手動設定必要",[43,1976,1977,1980,1983],{},[61,1978,1979],{},"カスタマイズ",[61,1981,1982],{},"限定的",[61,1984,1985],{},"柔軟",[43,1987,1988,1991,1994],{},[61,1989,1990],{},"Cloudflare対応",[61,1992,1993],{},"❌",[61,1995,1996],{},"✅",[19,1998,1999,2002],{},[25,2000,2001],{},"結論",": Vercel以外（Cloudflare等）では Satori + Resvg を使用。",[30,2004],{},[115,2006,2008],{"id":2007},"q11-cloudflare-pagesでも動くのか","Q11: Cloudflare Pagesでも動くのか？",[19,2010,2011,2013],{},[25,2012,165],{},": はい、Cloudflare Pages Functions（内部的にWorkers）で動作する。",[141,2015,2018],{"className":2016,"code":2017,"language":146},[144],"apps/web/\n└── server/\n    └── api/\n        └── og/\n            └── japanese-quiz.ts  ← Nitroがこれを自動的にWorkerに変換\n",[148,2019,2017],{"__ignoreMap":150},[19,2021,2022,2023,2026,2027,2030],{},"NuxtのNitroプリセット",[148,2024,2025],{},"cloudflare_pages","を使用している場合、",[148,2028,2029],{},"server/api/","配下のファイルは自動的にPages Functionsとしてデプロイされる。",[30,2032],{},[15,2034,2036],{"id":2035},"実装例nuxt-cloudflare-pages","実装例（Nuxt + Cloudflare Pages）",[141,2038,2040],{"className":254,"code":2039,"language":256,"meta":150,"style":150},"// server/api/og/japanese-quiz.ts\nimport satori from 'satori'\nimport { Resvg } from '@resvg/resvg-js'\n\nexport default defineEventHandler(async (event) => {\n  const { category, correct, total } = getQuery(event)\n\n  // カテゴリ名のマッピング\n  const categoryNames: Record\u003Cstring, string> = {\n    'modification-order': '修飾語の順序',\n    'punctuation': '句読点の打ち方',\n    'particles': '助詞の使い方',\n  }\n\n  const categoryName = categoryNames[category as string] || 'クイズ'\n\n  // フォント読み込み（事前にサブセット化推奨）\n  const fontData = await fetch(\n    'https://your-cdn.com/fonts/NotoSansJP-Bold-subset.ttf'\n  ).then(res => res.arrayBuffer())\n\n  // SVG生成\n  const svg = await satori(\n    {\n      type: 'div',\n      props: {\n        style: {\n          width: '100%',\n          height: '100%',\n          display: 'flex',\n          flexDirection: 'column',\n          alignItems: 'center',\n          justifyContent: 'center',\n          background: 'linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%)',\n          fontFamily: 'Noto Sans JP',\n          color: 'white',\n        },\n        children: [\n          {\n            type: 'div',\n            props: {\n              style: { fontSize: 48, marginBottom: 20 },\n              children: `📝 ${categoryName}クイズ`,\n            },\n          },\n          {\n            type: 'div',\n            props: {\n              style: { fontSize: 72, fontWeight: 'bold' },\n              children: `🎯 ${correct} / ${total} 問正解`,\n            },\n          },\n          {\n            type: 'div',\n            props: {\n              style: {\n                position: 'absolute',\n                bottom: 40,\n                right: 40,\n                fontSize: 24,\n                opacity: 0.8,\n              },\n              children: 'eurekapu.com',\n            },\n          },\n        ],\n      },\n    },\n    {\n      width: 1200,\n      height: 630,\n      fonts: [\n        {\n          name: 'Noto Sans JP',\n          data: fontData,\n          weight: 700,\n          style: 'normal',\n        },\n      ],\n    }\n  )\n\n  // PNG変換\n  const resvg = new Resvg(svg)\n  const pngBuffer = resvg.render().asPng()\n\n  // レスポンス\n  return new Response(pngBuffer, {\n    headers: {\n      'Content-Type': 'image/png',\n      'Cache-Control': 'public, max-age=86400',\n    },\n  })\n})\n",[148,2041,2042,2047,2061,2079,2083,2108,2138,2142,2147,2173,2194,2214,2234,2238,2242,2278,2282,2287,2301,2310,2331,2335,2340,2354,2359,2374,2381,2387,2403,2419,2436,2453,2470,2486,2503,2519,2536,2542,2550,2556,2572,2580,2610,2635,2641,2647,2652,2667,2674,2704,2738,2743,2748,2753,2768,2775,2782,2799,2812,2824,2837,2850,2856,2872,2877,2882,2888,2893,2899,2904,2916,2928,2936,2942,2958,2970,2982,2998,3003,3009,3014,3019,3024,3030,3049,3070,3075,3081,3098,3106,3126,3145,3150,3156],{"__ignoreMap":150},[260,2043,2044],{"class":262,"line":263},[260,2045,2046],{"class":298},"// server/api/og/japanese-quiz.ts\n",[260,2048,2049,2051,2053,2055,2057,2059],{"class":262,"line":288},[260,2050,267],{"class":266},[260,2052,271],{"class":270},[260,2054,274],{"class":266},[260,2056,278],{"class":277},[260,2058,282],{"class":281},[260,2060,285],{"class":277},[260,2062,2063,2065,2067,2069,2071,2073,2075,2077],{"class":262,"line":295},[260,2064,267],{"class":266},[260,2066,674],{"class":312},[260,2068,677],{"class":270},[260,2070,680],{"class":312},[260,2072,274],{"class":266},[260,2074,278],{"class":277},[260,2076,687],{"class":281},[260,2078,285],{"class":277},[260,2080,2081],{"class":262,"line":302},[260,2082,292],{"emptyLinePlaceholder":291},[260,2084,2085,2087,2089,2092,2094,2097,2099,2102,2104,2106],{"class":262,"line":337},[260,2086,1076],{"class":266},[260,2088,1079],{"class":266},[260,2090,2091],{"class":319}," defineEventHandler",[260,2093,323],{"class":312},[260,2095,2096],{"class":305},"async",[260,2098,1213],{"class":312},[260,2100,2101],{"class":270},"event",[260,2103,1600],{"class":312},[260,2105,351],{"class":312},[260,2107,741],{"class":312},[260,2109,2110,2113,2115,2117,2119,2121,2123,2125,2127,2129,2132,2134,2136],{"class":262,"line":366},[260,2111,2112],{"class":305},"  const ",[260,2114,1497],{"class":312},[260,2116,1500],{"class":270},[260,2118,738],{"class":312},[260,2120,1505],{"class":270},[260,2122,738],{"class":312},[260,2124,1510],{"class":270},[260,2126,680],{"class":312},[260,2128,313],{"class":312},[260,2130,2131],{"class":319}," getQuery",[260,2133,323],{"class":312},[260,2135,2101],{"class":270},[260,2137,334],{"class":312},[260,2139,2140],{"class":262,"line":371},[260,2141,292],{"emptyLinePlaceholder":291},[260,2143,2144],{"class":262,"line":388},[260,2145,2146],{"class":298},"  // カテゴリ名のマッピング\n",[260,2148,2149,2151,2154,2156,2159,2161,2164,2166,2168,2171],{"class":262,"line":433},[260,2150,2112],{"class":305},[260,2152,2153],{"class":270},"categoryNames",[260,2155,406],{"class":312},[260,2157,2158],{"class":1098},"Record",[260,2160,422],{"class":312},[260,2162,2163],{"class":1098},"string",[260,2165,1102],{"class":312},[260,2167,2163],{"class":1098},[260,2169,2170],{"class":312},"> =",[260,2172,741],{"class":312},[260,2174,2175,2178,2181,2183,2185,2187,2190,2192],{"class":262,"line":439},[260,2176,2177],{"class":277},"    '",[260,2179,2180],{"class":281},"modification-order",[260,2182,326],{"class":277},[260,2184,406],{"class":312},[260,2186,326],{"class":277},[260,2188,2189],{"class":281},"修飾語の順序",[260,2191,326],{"class":277},[260,2193,452],{"class":312},[260,2195,2196,2198,2201,2203,2205,2207,2210,2212],{"class":262,"line":455},[260,2197,2177],{"class":277},[260,2199,2200],{"class":281},"punctuation",[260,2202,326],{"class":277},[260,2204,406],{"class":312},[260,2206,326],{"class":277},[260,2208,2209],{"class":281},"句読点の打ち方",[260,2211,326],{"class":277},[260,2213,452],{"class":312},[260,2215,2216,2218,2221,2223,2225,2227,2230,2232],{"class":262,"line":468},[260,2217,2177],{"class":277},[260,2219,2220],{"class":281},"particles",[260,2222,326],{"class":277},[260,2224,406],{"class":312},[260,2226,326],{"class":277},[260,2228,2229],{"class":281},"助詞の使い方",[260,2231,326],{"class":277},[260,2233,452],{"class":312},[260,2235,2236],{"class":262,"line":477},[260,2237,556],{"class":312},[260,2239,2240],{"class":262,"line":483},[260,2241,292],{"emptyLinePlaceholder":291},[260,2243,2244,2246,2249,2251,2254,2257,2259,2262,2265,2268,2271,2273,2276],{"class":262,"line":499},[260,2245,2112],{"class":305},[260,2247,2248],{"class":270},"categoryName",[260,2250,313],{"class":312},[260,2252,2253],{"class":270}," categoryNames",[260,2255,2256],{"class":312},"[",[260,2258,1552],{"class":270},[260,2260,2261],{"class":266}," as",[260,2263,2264],{"class":1098}," string",[260,2266,2267],{"class":312},"]",[260,2269,2270],{"class":305}," || ",[260,2272,326],{"class":277},[260,2274,2275],{"class":281},"クイズ",[260,2277,285],{"class":277},[260,2279,2280],{"class":262,"line":511},[260,2281,292],{"emptyLinePlaceholder":291},[260,2283,2284],{"class":262,"line":524},[260,2285,2286],{"class":298},"  // フォント読み込み（事前にサブセット化推奨）\n",[260,2288,2289,2291,2293,2295,2297,2299],{"class":262,"line":541},[260,2290,2112],{"class":305},[260,2292,309],{"class":270},[260,2294,313],{"class":312},[260,2296,316],{"class":266},[260,2298,320],{"class":319},[260,2300,385],{"class":312},[260,2302,2303,2305,2308],{"class":262,"line":547},[260,2304,2177],{"class":277},[260,2306,2307],{"class":281},"https://your-cdn.com/fonts/NotoSansJP-Bold-subset.ttf",[260,2309,285],{"class":277},[260,2311,2312,2315,2317,2319,2321,2323,2325,2327,2329],{"class":262,"line":553},[260,2313,2314],{"class":312},"  ).",[260,2316,343],{"class":319},[260,2318,323],{"class":312},[260,2320,348],{"class":270},[260,2322,351],{"class":312},[260,2324,354],{"class":270},[260,2326,357],{"class":312},[260,2328,360],{"class":319},[260,2330,363],{"class":312},[260,2332,2333],{"class":262,"line":559},[260,2334,292],{"emptyLinePlaceholder":291},[260,2336,2337],{"class":262,"line":1347},[260,2338,2339],{"class":298},"  // SVG生成\n",[260,2341,2342,2344,2346,2348,2350,2352],{"class":262,"line":1352},[260,2343,2112],{"class":305},[260,2345,376],{"class":270},[260,2347,313],{"class":312},[260,2349,316],{"class":266},[260,2351,271],{"class":319},[260,2353,385],{"class":312},[260,2355,2356],{"class":262,"line":1358},[260,2357,2358],{"class":312},"    {\n",[260,2360,2361,2364,2366,2368,2370,2372],{"class":262,"line":1394},[260,2362,2363],{"class":442},"      type",[260,2365,406],{"class":312},[260,2367,326],{"class":277},[260,2369,394],{"class":281},[260,2371,326],{"class":277},[260,2373,452],{"class":312},[260,2375,2376,2379],{"class":262,"line":1399},[260,2377,2378],{"class":442},"      props",[260,2380,749],{"class":312},[260,2382,2383,2385],{"class":262,"line":1407},[260,2384,527],{"class":442},[260,2386,749],{"class":312},[260,2388,2389,2392,2394,2396,2399,2401],{"class":262,"line":1412},[260,2390,2391],{"class":442},"          width",[260,2393,406],{"class":312},[260,2395,326],{"class":277},[260,2397,2398],{"class":281},"100%",[260,2400,326],{"class":277},[260,2402,452],{"class":312},[260,2404,2406,2409,2411,2413,2415,2417],{"class":262,"line":2405},29,[260,2407,2408],{"class":442},"          height",[260,2410,406],{"class":312},[260,2412,326],{"class":277},[260,2414,2398],{"class":281},[260,2416,326],{"class":277},[260,2418,452],{"class":312},[260,2420,2422,2425,2427,2429,2432,2434],{"class":262,"line":2421},30,[260,2423,2424],{"class":442},"          display",[260,2426,406],{"class":312},[260,2428,326],{"class":277},[260,2430,2431],{"class":281},"flex",[260,2433,326],{"class":277},[260,2435,452],{"class":312},[260,2437,2439,2442,2444,2446,2449,2451],{"class":262,"line":2438},31,[260,2440,2441],{"class":442},"          flexDirection",[260,2443,406],{"class":312},[260,2445,326],{"class":277},[260,2447,2448],{"class":281},"column",[260,2450,326],{"class":277},[260,2452,452],{"class":312},[260,2454,2456,2459,2461,2463,2466,2468],{"class":262,"line":2455},32,[260,2457,2458],{"class":442},"          alignItems",[260,2460,406],{"class":312},[260,2462,326],{"class":277},[260,2464,2465],{"class":281},"center",[260,2467,326],{"class":277},[260,2469,452],{"class":312},[260,2471,2473,2476,2478,2480,2482,2484],{"class":262,"line":2472},33,[260,2474,2475],{"class":442},"          justifyContent",[260,2477,406],{"class":312},[260,2479,326],{"class":277},[260,2481,2465],{"class":281},[260,2483,326],{"class":277},[260,2485,452],{"class":312},[260,2487,2489,2492,2494,2496,2499,2501],{"class":262,"line":2488},34,[260,2490,2491],{"class":442},"          background",[260,2493,406],{"class":312},[260,2495,326],{"class":277},[260,2497,2498],{"class":281},"linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%)",[260,2500,326],{"class":277},[260,2502,452],{"class":312},[260,2504,2506,2509,2511,2513,2515,2517],{"class":262,"line":2505},35,[260,2507,2508],{"class":442},"          fontFamily",[260,2510,406],{"class":312},[260,2512,326],{"class":277},[260,2514,411],{"class":281},[260,2516,326],{"class":277},[260,2518,452],{"class":312},[260,2520,2522,2525,2527,2529,2532,2534],{"class":262,"line":2521},36,[260,2523,2524],{"class":442},"          color",[260,2526,406],{"class":312},[260,2528,326],{"class":277},[260,2530,2531],{"class":281},"white",[260,2533,326],{"class":277},[260,2535,452],{"class":312},[260,2537,2539],{"class":262,"line":2538},37,[260,2540,2541],{"class":312},"        },\n",[260,2543,2545,2548],{"class":262,"line":2544},38,[260,2546,2547],{"class":442},"        children",[260,2549,474],{"class":312},[260,2551,2553],{"class":262,"line":2552},39,[260,2554,2555],{"class":312},"          {\n",[260,2557,2559,2562,2564,2566,2568,2570],{"class":262,"line":2558},40,[260,2560,2561],{"class":442},"            type",[260,2563,406],{"class":312},[260,2565,326],{"class":277},[260,2567,394],{"class":281},[260,2569,326],{"class":277},[260,2571,452],{"class":312},[260,2573,2575,2578],{"class":262,"line":2574},41,[260,2576,2577],{"class":442},"            props",[260,2579,749],{"class":312},[260,2581,2583,2586,2589,2592,2594,2597,2599,2602,2604,2607],{"class":262,"line":2582},42,[260,2584,2585],{"class":442},"              style",[260,2587,2588],{"class":312},": { ",[260,2590,2591],{"class":442},"fontSize",[260,2593,406],{"class":312},[260,2595,2596],{"class":448},"48",[260,2598,1102],{"class":312},[260,2600,2601],{"class":442},"marginBottom",[260,2603,406],{"class":312},[260,2605,2606],{"class":448},"20",[260,2608,2609],{"class":312}," },\n",[260,2611,2613,2616,2618,2620,2623,2625,2627,2629,2631,2633],{"class":262,"line":2612},43,[260,2614,2615],{"class":442},"              children",[260,2617,406],{"class":312},[260,2619,1543],{"class":277},[260,2621,2622],{"class":281},"📝 ",[260,2624,1549],{"class":266},[260,2626,2248],{"class":281},[260,2628,1555],{"class":266},[260,2630,2275],{"class":281},[260,2632,1543],{"class":277},[260,2634,452],{"class":312},[260,2636,2638],{"class":262,"line":2637},44,[260,2639,2640],{"class":312},"            },\n",[260,2642,2644],{"class":262,"line":2643},45,[260,2645,2646],{"class":312},"          },\n",[260,2648,2650],{"class":262,"line":2649},46,[260,2651,2555],{"class":312},[260,2653,2655,2657,2659,2661,2663,2665],{"class":262,"line":2654},47,[260,2656,2561],{"class":442},[260,2658,406],{"class":312},[260,2660,326],{"class":277},[260,2662,394],{"class":281},[260,2664,326],{"class":277},[260,2666,452],{"class":312},[260,2668,2670,2672],{"class":262,"line":2669},48,[260,2671,2577],{"class":442},[260,2673,749],{"class":312},[260,2675,2677,2679,2681,2683,2685,2688,2690,2693,2695,2697,2700,2702],{"class":262,"line":2676},49,[260,2678,2585],{"class":442},[260,2680,2588],{"class":312},[260,2682,2591],{"class":442},[260,2684,406],{"class":312},[260,2686,2687],{"class":448},"72",[260,2689,1102],{"class":312},[260,2691,2692],{"class":442},"fontWeight",[260,2694,406],{"class":312},[260,2696,326],{"class":277},[260,2698,2699],{"class":281},"bold",[260,2701,326],{"class":277},[260,2703,2609],{"class":312},[260,2705,2707,2709,2711,2713,2716,2718,2720,2722,2725,2727,2729,2731,2734,2736],{"class":262,"line":2706},50,[260,2708,2615],{"class":442},[260,2710,406],{"class":312},[260,2712,1543],{"class":277},[260,2714,2715],{"class":281},"🎯 ",[260,2717,1549],{"class":266},[260,2719,1563],{"class":281},[260,2721,1555],{"class":266},[260,2723,2724],{"class":281}," / ",[260,2726,1549],{"class":266},[260,2728,1573],{"class":281},[260,2730,1555],{"class":266},[260,2732,2733],{"class":281}," 問正解",[260,2735,1543],{"class":277},[260,2737,452],{"class":312},[260,2739,2741],{"class":262,"line":2740},51,[260,2742,2640],{"class":312},[260,2744,2746],{"class":262,"line":2745},52,[260,2747,2646],{"class":312},[260,2749,2751],{"class":262,"line":2750},53,[260,2752,2555],{"class":312},[260,2754,2756,2758,2760,2762,2764,2766],{"class":262,"line":2755},54,[260,2757,2561],{"class":442},[260,2759,406],{"class":312},[260,2761,326],{"class":277},[260,2763,394],{"class":281},[260,2765,326],{"class":277},[260,2767,452],{"class":312},[260,2769,2771,2773],{"class":262,"line":2770},55,[260,2772,2577],{"class":442},[260,2774,749],{"class":312},[260,2776,2778,2780],{"class":262,"line":2777},56,[260,2779,2585],{"class":442},[260,2781,749],{"class":312},[260,2783,2785,2788,2790,2792,2795,2797],{"class":262,"line":2784},57,[260,2786,2787],{"class":442},"                position",[260,2789,406],{"class":312},[260,2791,326],{"class":277},[260,2793,2794],{"class":281},"absolute",[260,2796,326],{"class":277},[260,2798,452],{"class":312},[260,2800,2802,2805,2807,2810],{"class":262,"line":2801},58,[260,2803,2804],{"class":442},"                bottom",[260,2806,406],{"class":312},[260,2808,2809],{"class":448},"40",[260,2811,452],{"class":312},[260,2813,2815,2818,2820,2822],{"class":262,"line":2814},59,[260,2816,2817],{"class":442},"                right",[260,2819,406],{"class":312},[260,2821,2809],{"class":448},[260,2823,452],{"class":312},[260,2825,2827,2830,2832,2835],{"class":262,"line":2826},60,[260,2828,2829],{"class":442},"                fontSize",[260,2831,406],{"class":312},[260,2833,2834],{"class":448},"24",[260,2836,452],{"class":312},[260,2838,2840,2843,2845,2848],{"class":262,"line":2839},61,[260,2841,2842],{"class":442},"                opacity",[260,2844,406],{"class":312},[260,2846,2847],{"class":448},"0.8",[260,2849,452],{"class":312},[260,2851,2853],{"class":262,"line":2852},62,[260,2854,2855],{"class":312},"              },\n",[260,2857,2859,2861,2863,2865,2868,2870],{"class":262,"line":2858},63,[260,2860,2615],{"class":442},[260,2862,406],{"class":312},[260,2864,326],{"class":277},[260,2866,2867],{"class":281},"eurekapu.com",[260,2869,326],{"class":277},[260,2871,452],{"class":312},[260,2873,2875],{"class":262,"line":2874},64,[260,2876,2640],{"class":312},[260,2878,2880],{"class":262,"line":2879},65,[260,2881,2646],{"class":312},[260,2883,2885],{"class":262,"line":2884},66,[260,2886,2887],{"class":312},"        ],\n",[260,2889,2891],{"class":262,"line":2890},67,[260,2892,544],{"class":312},[260,2894,2896],{"class":262,"line":2895},68,[260,2897,2898],{"class":312},"    },\n",[260,2900,2902],{"class":262,"line":2901},69,[260,2903,2358],{"class":312},[260,2905,2907,2910,2912,2914],{"class":262,"line":2906},70,[260,2908,2909],{"class":442},"      width",[260,2911,406],{"class":312},[260,2913,449],{"class":448},[260,2915,452],{"class":312},[260,2917,2919,2922,2924,2926],{"class":262,"line":2918},71,[260,2920,2921],{"class":442},"      height",[260,2923,406],{"class":312},[260,2925,463],{"class":448},[260,2927,452],{"class":312},[260,2929,2931,2934],{"class":262,"line":2930},72,[260,2932,2933],{"class":442},"      fonts",[260,2935,474],{"class":312},[260,2937,2939],{"class":262,"line":2938},73,[260,2940,2941],{"class":312},"        {\n",[260,2943,2945,2948,2950,2952,2954,2956],{"class":262,"line":2944},74,[260,2946,2947],{"class":442},"          name",[260,2949,406],{"class":312},[260,2951,326],{"class":277},[260,2953,411],{"class":281},[260,2955,326],{"class":277},[260,2957,452],{"class":312},[260,2959,2961,2964,2966,2968],{"class":262,"line":2960},75,[260,2962,2963],{"class":442},"          data",[260,2965,406],{"class":312},[260,2967,309],{"class":270},[260,2969,452],{"class":312},[260,2971,2973,2976,2978,2980],{"class":262,"line":2972},76,[260,2974,2975],{"class":442},"          weight",[260,2977,406],{"class":312},[260,2979,519],{"class":448},[260,2981,452],{"class":312},[260,2983,2985,2988,2990,2992,2994,2996],{"class":262,"line":2984},77,[260,2986,2987],{"class":442},"          style",[260,2989,406],{"class":312},[260,2991,326],{"class":277},[260,2993,534],{"class":281},[260,2995,326],{"class":277},[260,2997,452],{"class":312},[260,2999,3001],{"class":262,"line":3000},78,[260,3002,2541],{"class":312},[260,3004,3006],{"class":262,"line":3005},79,[260,3007,3008],{"class":312},"      ],\n",[260,3010,3012],{"class":262,"line":3011},80,[260,3013,1230],{"class":312},[260,3015,3017],{"class":262,"line":3016},81,[260,3018,1928],{"class":312},[260,3020,3022],{"class":262,"line":3021},82,[260,3023,292],{"emptyLinePlaceholder":291},[260,3025,3027],{"class":262,"line":3026},83,[260,3028,3029],{"class":298},"  // PNG変換\n",[260,3031,3033,3035,3037,3039,3041,3043,3045,3047],{"class":262,"line":3032},84,[260,3034,2112],{"class":305},[260,3036,723],{"class":270},[260,3038,313],{"class":312},[260,3040,728],{"class":305},[260,3042,731],{"class":319},[260,3044,323],{"class":312},[260,3046,376],{"class":270},[260,3048,334],{"class":312},[260,3050,3052,3054,3056,3058,3060,3062,3064,3066,3068],{"class":262,"line":3051},85,[260,3053,2112],{"class":305},[260,3055,793],{"class":270},[260,3057,313],{"class":312},[260,3059,798],{"class":270},[260,3061,357],{"class":312},[260,3063,803],{"class":319},[260,3065,806],{"class":312},[260,3067,809],{"class":319},[260,3069,812],{"class":312},[260,3071,3073],{"class":262,"line":3072},86,[260,3074,292],{"emptyLinePlaceholder":291},[260,3076,3078],{"class":262,"line":3077},87,[260,3079,3080],{"class":298},"  // レスポンス\n",[260,3082,3084,3086,3088,3090,3092,3094,3096],{"class":262,"line":3083},88,[260,3085,1870],{"class":266},[260,3087,1873],{"class":305},[260,3089,1277],{"class":319},[260,3091,323],{"class":312},[260,3093,793],{"class":270},[260,3095,738],{"class":312},[260,3097,741],{"class":312},[260,3099,3101,3104],{"class":262,"line":3100},89,[260,3102,3103],{"class":442},"    headers",[260,3105,749],{"class":312},[260,3107,3109,3112,3114,3116,3118,3120,3122,3124],{"class":262,"line":3108},90,[260,3110,3111],{"class":277},"      '",[260,3113,1299],{"class":281},[260,3115,326],{"class":277},[260,3117,406],{"class":312},[260,3119,326],{"class":277},[260,3121,1308],{"class":281},[260,3123,326],{"class":277},[260,3125,452],{"class":312},[260,3127,3129,3131,3133,3135,3137,3139,3141,3143],{"class":262,"line":3128},91,[260,3130,3111],{"class":277},[260,3132,1319],{"class":281},[260,3134,326],{"class":277},[260,3136,406],{"class":312},[260,3138,326],{"class":277},[260,3140,1328],{"class":281},[260,3142,326],{"class":277},[260,3144,452],{"class":312},[260,3146,3148],{"class":262,"line":3147},92,[260,3149,2898],{"class":312},[260,3151,3153],{"class":262,"line":3152},93,[260,3154,3155],{"class":312},"  })\n",[260,3157,3159],{"class":262,"line":3158},94,[260,3160,786],{"class":312},[30,3162],{},[15,3164,3165],{"id":3165},"まとめ",[37,3167,3168,3177],{},[40,3169,3170],{},[43,3171,3172,3174],{},[46,3173,48],{},[46,3175,3176],{},"推奨",[56,3178,3179,3185,3193,3201,3209],{},[43,3180,3181,3183],{},[61,3182,203],{},[61,3184,1950],{},[43,3186,3187,3190],{},[61,3188,3189],{},"ホスティング",[61,3191,3192],{},"Cloudflare Workers/Pages",[43,3194,3195,3198],{},[61,3196,3197],{},"フォント",[61,3199,3200],{},"Noto Sans JP（サブセット化）",[43,3202,3203,3206],{},[61,3204,3205],{},"キャッシュ",[61,3207,3208],{},"24時間以上",[43,3210,3211,3214],{},[61,3212,3213],{},"画像サイズ",[61,3215,3216],{},"1200 x 630 px",[19,3218,3219],{},"一度仕組みを作れば様々な場面で再利用できる動的OGP画像生成は、クイズ結果だけでなくユーザープロフィール、証明書、ランキングなど応用範囲が広い。",[3221,3222,3223],"style",{},"html pre.shiki code .sHkkW, html code.shiki .sHkkW{--shiki-default:#1E754F;--shiki-dark:#1E754F}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 .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}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 .sqvqQ, html code.shiki .sqvqQ{--shiki-default:#99841877;--shiki-dark:#99841877}html pre.shiki code .sSkh3, html code.shiki .sSkh3{--shiki-default:#2E8F82;--shiki-dark:#2E8F82}",{"title":150,"searchDepth":288,"depth":288,"links":3225},[3226,3227,3230,3231,3244,3245],{"id":17,"depth":288,"text":17},{"id":34,"depth":288,"text":35,"children":3228},[3229],{"id":117,"depth":295,"text":118},{"id":139,"depth":288,"text":139},{"id":155,"depth":288,"text":156,"children":3232},[3233,3234,3235,3236,3237,3238,3239,3240,3241,3242,3243],{"id":159,"depth":295,"text":160},{"id":245,"depth":295,"text":246},{"id":582,"depth":295,"text":583},{"id":640,"depth":295,"text":641},{"id":817,"depth":295,"text":818},{"id":964,"depth":295,"text":965},{"id":1055,"depth":295,"text":1056},{"id":1430,"depth":295,"text":1431},{"id":1682,"depth":295,"text":1683},{"id":1773,"depth":295,"text":1774},{"id":2007,"depth":295,"text":2008},{"id":2035,"depth":288,"text":2036},{"id":3165,"depth":288,"text":3165},null,"クイズ結果などユーザー固有のデータを含むOGP画像を動的に生成する方法。Satori、Resvg、日本語フォント対応まで解説","md",{},"/dynamic-ogp-image-generation-guide",false,"2025-12-24T00:00:00.000Z",{"title":5,"description":3247},"2025-12/2025-12-24/dynamic-ogp-image-generation-guide",[3256,282,3257,3258,3259],"ogp","cloudflare-workers","dynamic-image","nuxt","VMLWOc1_o6234dTcllgsLVOv2s1FDHxcLs6HIre1Iz0",[],[3263,3267,3270,3274,3278],{"title":3264,"path":3265,"publishedAt":3266},"Cloudflare Workers Routes でOGPシェアURLのSEO問題を解決","/cloudflare-workers-routes-ogp-solution","2025-12-25T00:00:00.000Z",{"title":3268,"path":3269,"publishedAt":3266},"OGPシェアURL改善計画 - SEO効果を最大化する設計","/ogp-share-url-seo-improvement",{"title":3271,"path":3272,"publishedAt":3273},"移行サイトのSEO・AI-SEO改善を1日で回した — 画像altを480件、記事の要点を203本、OG画像を署名付きに","/wordpress-nuxt-ai-seo-phase-c","2026-07-27T00:00:00.000Z",{"title":3275,"path":3276,"publishedAt":3277},"PlaywrightでVueコンポーネントをOGP画像に変換する","/playwright-ogp-screenshot","2026-01-10T00:00:00.000Z",{"title":3279,"path":3280,"publishedAt":3252},"Nitroプリセット変更: cloudflare-pages-static → cloudflare-pages","/nitro-preset-change","https://log.eurekapu.com/og/blog/dynamic-ogp-image-generation-guide.png?v=2025-12-24T00%3A00%3A00.000Z&title=%E5%8B%95%E7%9A%84OGP%E7%94%BB%E5%83%8F%E7%94%9F%E6%88%90%E3%81%AE%E5%AE%9F%E8%A3%85%E3%82%AC%E3%82%A4%E3%83%89%20-%20Satori%20%2B%20Cloudflare%20Workers&author=Kei%20Komatsu&sig=e97dfc566cf7085f",1785654689801]