[{"data":1,"prerenderedAt":2484},["ShallowReactive",2],{"en-post-\u002Fen\u002Fnuxt3-seo-setup":3},{"id":4,"title":5,"body":6,"description":2473,"extension":2474,"meta":2475,"navigation":253,"path":2479,"seo":2480,"sitemap":2481,"stem":2482,"__hash__":2483},"blogEn\u002Fen\u002Fnuxt3-seo-setup.md","Nuxt 3 SEO Setup: Sitemap, Canonical, hreflang, and Structured Data (JSON-LD)",{"type":7,"value":8,"toc":2455},"minimark",[9,14,18,35,43,94,97,101,111,129,208,525,531,536,543,658,680,696,700,714,716,720,725,731,876,880,902,916,1054,1081,1083,1087,1095,1098,1256,1263,1439,1486,1495,1497,1501,1506,1512,1676,1702,1933,1937,1952,2068,2092,2094,2098,2103,2205,2219,2230,2232,2236,2250,2301,2304,2306,2310,2388,2395,2399,2451],[10,11,13],"h2",{"id":12},"what-this-post-covers","What this post covers",[15,16,17],"p",{},"While investigating Google indexing problems on this blog (Nuxt 3 + Nuxt Content, deployed on Vercel), I fixed its SEO setup piece by piece. The investigation itself is in the previous two posts:",[19,20,21,29],"ul",{},[22,23,24],"li",{},[25,26,28],"a",{"href":27},"\u002Fen\u002Fsearch-console-discovered-crawled-not-indexed","Search Console \"Discovered \u002F Crawled - currently not indexed\"",[22,30,31],{},[25,32,34],{"href":33},"\u002Fen\u002Fload-more-button-pagination-seo","\"Load More\" buttons and SEO",[15,36,37,38,42],{},"This post covers ",[39,40,41],"strong",{},"the setup I actually applied, in code."," Versions:",[44,45,46,59],"table",{},[47,48,49],"thead",{},[50,51,52,56],"tr",{},[53,54,55],"th",{},"Package",[53,57,58],{},"Version",[60,61,62,74,84],"tbody",{},[50,63,64,71],{},[65,66,67],"td",{},[68,69,70],"code",{},"nuxt",[65,72,73],{},"3.21",[50,75,76,81],{},[65,77,78],{},[68,79,80],{},"@nuxt\u002Fcontent",[65,82,83],{},"3.16",[50,85,86,91],{},[65,87,88],{},[68,89,90],{},"@nuxtjs\u002Fsitemap",[65,92,93],{},"8.6",[95,96],"hr",{},[10,98,100],{"id":99},"_1-sitemap-add-nuxt-content-posts-automatically","1. Sitemap: add Nuxt Content posts automatically",[102,103,104],"blockquote",{},[15,105,106,107,110],{},"A sitemap is a ",[39,108,109],{},"list"," telling Google \"these URLs exist.\" To avoid editing it by hand for every post, generate it from Nuxt Content collections.",[15,112,113,114,116,117,120,121,124,125,128],{},"Add the ",[68,115,90],{}," module, then wrap collections with ",[68,118,119],{},"asSitemapCollection"," in ",[68,122,123],{},"content.config.ts",", and that collection's posts go into ",[68,126,127],{},"\u002Fsitemap.xml",".",[130,131,136],"pre",{"className":132,"code":133,"language":134,"meta":135,"style":135},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  site: { url: \"https:\u002F\u002Fiambilly-blog.vercel.app\" }, \u002F\u002F used for absolute URLs in the sitemap\n  modules: [\"@nuxtjs\u002Fsitemap\", \"@nuxt\u002Fcontent\"],      \u002F\u002F sitemap before content\n});\n","ts","",[68,137,138,147,165,181,202],{"__ignoreMap":135},[139,140,143],"span",{"class":141,"line":142},"line",1,[139,144,146],{"class":145},"sJ8bj","\u002F\u002F nuxt.config.ts\n",[139,148,150,154,157,161],{"class":141,"line":149},2,[139,151,153],{"class":152},"szBVR","export",[139,155,156],{"class":152}," default",[139,158,160],{"class":159},"sScJk"," defineNuxtConfig",[139,162,164],{"class":163},"sVt8B","({\n",[139,166,168,171,175,178],{"class":141,"line":167},3,[139,169,170],{"class":163},"  site: { url: ",[139,172,174],{"class":173},"sZZnC","\"https:\u002F\u002Fiambilly-blog.vercel.app\"",[139,176,177],{"class":163}," }, ",[139,179,180],{"class":145},"\u002F\u002F used for absolute URLs in the sitemap\n",[139,182,184,187,190,193,196,199],{"class":141,"line":183},4,[139,185,186],{"class":163},"  modules: [",[139,188,189],{"class":173},"\"@nuxtjs\u002Fsitemap\"",[139,191,192],{"class":163},", ",[139,194,195],{"class":173},"\"@nuxt\u002Fcontent\"",[139,197,198],{"class":163},"],      ",[139,200,201],{"class":145},"\u002F\u002F sitemap before content\n",[139,203,205],{"class":141,"line":204},5,[139,206,207],{"class":163},"});\n",[130,209,211],{"className":132,"code":210,"language":134,"meta":135,"style":135},"\u002F\u002F content.config.ts\nimport { defineContentConfig, defineCollection } from \"@nuxt\u002Fcontent\";\nimport { asSitemapCollection } from \"@nuxtjs\u002Fsitemap\u002Fcontent\";\n\nexport default defineContentConfig({\n  collections: {\n    ko: defineCollection(\n      asSitemapCollection(\n        { type: \"page\", source: \"ko\u002F*.md\" },\n        {\n          name: \"ko\",\n          \u002F\u002F turn the frontmatter date (2026.10.14) into lastmod (2026-10-14)\n          onUrl(url, entry) {\n            const raw = entry.date ?? entry.meta?.date;\n            const iso = raw ? String(raw).trim().replace(\u002F\\.\u002Fg, \"-\") : \"\";\n            if (\u002F^\\d{4}-\\d{2}-\\d{2}$\u002F.test(iso)) url.lastmod = iso;\n          },\n        }\n      )\n    ),\n  },\n});\n",[68,212,213,218,235,249,255,266,272,284,292,310,316,328,334,355,377,438,490,496,502,508,514,520],{"__ignoreMap":135},[139,214,215],{"class":141,"line":142},[139,216,217],{"class":145},"\u002F\u002F content.config.ts\n",[139,219,220,223,226,229,232],{"class":141,"line":149},[139,221,222],{"class":152},"import",[139,224,225],{"class":163}," { defineContentConfig, defineCollection } ",[139,227,228],{"class":152},"from",[139,230,231],{"class":173}," \"@nuxt\u002Fcontent\"",[139,233,234],{"class":163},";\n",[139,236,237,239,242,244,247],{"class":141,"line":167},[139,238,222],{"class":152},[139,240,241],{"class":163}," { asSitemapCollection } ",[139,243,228],{"class":152},[139,245,246],{"class":173}," \"@nuxtjs\u002Fsitemap\u002Fcontent\"",[139,248,234],{"class":163},[139,250,251],{"class":141,"line":183},[139,252,254],{"emptyLinePlaceholder":253},true,"\n",[139,256,257,259,261,264],{"class":141,"line":204},[139,258,153],{"class":152},[139,260,156],{"class":152},[139,262,263],{"class":159}," defineContentConfig",[139,265,164],{"class":163},[139,267,269],{"class":141,"line":268},6,[139,270,271],{"class":163},"  collections: {\n",[139,273,275,278,281],{"class":141,"line":274},7,[139,276,277],{"class":163},"    ko: ",[139,279,280],{"class":159},"defineCollection",[139,282,283],{"class":163},"(\n",[139,285,287,290],{"class":141,"line":286},8,[139,288,289],{"class":159},"      asSitemapCollection",[139,291,283],{"class":163},[139,293,295,298,301,304,307],{"class":141,"line":294},9,[139,296,297],{"class":163},"        { type: ",[139,299,300],{"class":173},"\"page\"",[139,302,303],{"class":163},", source: ",[139,305,306],{"class":173},"\"ko\u002F*.md\"",[139,308,309],{"class":163}," },\n",[139,311,313],{"class":141,"line":312},10,[139,314,315],{"class":163},"        {\n",[139,317,319,322,325],{"class":141,"line":318},11,[139,320,321],{"class":163},"          name: ",[139,323,324],{"class":173},"\"ko\"",[139,326,327],{"class":163},",\n",[139,329,331],{"class":141,"line":330},12,[139,332,333],{"class":145},"          \u002F\u002F turn the frontmatter date (2026.10.14) into lastmod (2026-10-14)\n",[139,335,337,340,343,347,349,352],{"class":141,"line":336},13,[139,338,339],{"class":159},"          onUrl",[139,341,342],{"class":163},"(",[139,344,346],{"class":345},"s4XuR","url",[139,348,192],{"class":163},[139,350,351],{"class":345},"entry",[139,353,354],{"class":163},") {\n",[139,356,358,361,365,368,371,374],{"class":141,"line":357},14,[139,359,360],{"class":152},"            const",[139,362,364],{"class":363},"sj4cs"," raw",[139,366,367],{"class":152}," =",[139,369,370],{"class":163}," entry.date ",[139,372,373],{"class":152},"??",[139,375,376],{"class":163}," entry.meta?.date;\n",[139,378,380,382,385,387,390,393,396,399,402,405,408,410,413,417,419,422,424,427,430,433,436],{"class":141,"line":379},15,[139,381,360],{"class":152},[139,383,384],{"class":363}," iso",[139,386,367],{"class":152},[139,388,389],{"class":163}," raw ",[139,391,392],{"class":152},"?",[139,394,395],{"class":159}," String",[139,397,398],{"class":163},"(raw).",[139,400,401],{"class":159},"trim",[139,403,404],{"class":163},"().",[139,406,407],{"class":159},"replace",[139,409,342],{"class":163},[139,411,412],{"class":173},"\u002F",[139,414,416],{"class":415},"snhLl","\\.",[139,418,412],{"class":173},[139,420,421],{"class":152},"g",[139,423,192],{"class":163},[139,425,426],{"class":173},"\"-\"",[139,428,429],{"class":163},") ",[139,431,432],{"class":152},":",[139,434,435],{"class":173}," \"\"",[139,437,234],{"class":163},[139,439,441,444,447,449,452,455,458,462,464,467,469,471,474,476,478,481,484,487],{"class":141,"line":440},16,[139,442,443],{"class":152},"            if",[139,445,446],{"class":163}," (",[139,448,412],{"class":173},[139,450,451],{"class":152},"^",[139,453,454],{"class":363},"\\d",[139,456,457],{"class":152},"{4}",[139,459,461],{"class":460},"sA_wV","-",[139,463,454],{"class":363},[139,465,466],{"class":152},"{2}",[139,468,461],{"class":460},[139,470,454],{"class":363},[139,472,473],{"class":152},"{2}$",[139,475,412],{"class":173},[139,477,128],{"class":163},[139,479,480],{"class":159},"test",[139,482,483],{"class":163},"(iso)) url.lastmod ",[139,485,486],{"class":152},"=",[139,488,489],{"class":163}," iso;\n",[139,491,493],{"class":141,"line":492},17,[139,494,495],{"class":163},"          },\n",[139,497,499],{"class":141,"line":498},18,[139,500,501],{"class":163},"        }\n",[139,503,505],{"class":141,"line":504},19,[139,506,507],{"class":163},"      )\n",[139,509,511],{"class":141,"line":510},20,[139,512,513],{"class":163},"    ),\n",[139,515,517],{"class":141,"line":516},21,[139,518,519],{"class":163},"  },\n",[139,521,523],{"class":141,"line":522},22,[139,524,207],{"class":163},[15,526,527,528,530],{},"Module order matters too. If ",[68,529,80],{}," comes first, the sitemap module warns that the integration may have issues.",[532,533,535],"h3",{"id":534},"gotcha-dont-use-outer-variables-inside-onurl","Gotcha: don't use outer variables inside onUrl",[15,537,538,539,542],{},"The English collection uses the same ",[68,540,541],{},"onUrl",", so it's tempting to write one helper and share it. But this breaks the build:",[130,544,546],{"className":132,"code":545,"language":134,"meta":135,"style":135},"\u002F\u002F ❌ calling a function defined outside onUrl\nconst toIsoDate = (raw) => String(raw).trim().replace(\u002F\\.\u002Fg, \"-\");\n\nasSitemapCollection(collection, {\n  name: \"ko\",\n  onUrl(url, entry) {\n    url.lastmod = toIsoDate(entry.date); \u002F\u002F build error\n  },\n});\n",[68,547,548,553,600,604,611,620,635,650,654],{"__ignoreMap":135},[139,549,550],{"class":141,"line":142},[139,551,552],{"class":145},"\u002F\u002F ❌ calling a function defined outside onUrl\n",[139,554,555,558,561,563,565,568,570,573,575,577,579,581,583,585,587,589,591,593,595,597],{"class":141,"line":149},[139,556,557],{"class":152},"const",[139,559,560],{"class":159}," toIsoDate",[139,562,367],{"class":152},[139,564,446],{"class":163},[139,566,567],{"class":345},"raw",[139,569,429],{"class":163},[139,571,572],{"class":152},"=>",[139,574,395],{"class":159},[139,576,398],{"class":163},[139,578,401],{"class":159},[139,580,404],{"class":163},[139,582,407],{"class":159},[139,584,342],{"class":163},[139,586,412],{"class":173},[139,588,416],{"class":415},[139,590,412],{"class":173},[139,592,421],{"class":152},[139,594,192],{"class":163},[139,596,426],{"class":173},[139,598,599],{"class":163},");\n",[139,601,602],{"class":141,"line":167},[139,603,254],{"emptyLinePlaceholder":253},[139,605,606,608],{"class":141,"line":183},[139,607,119],{"class":159},[139,609,610],{"class":163},"(collection, {\n",[139,612,613,616,618],{"class":141,"line":204},[139,614,615],{"class":163},"  name: ",[139,617,324],{"class":173},[139,619,327],{"class":163},[139,621,622,625,627,629,631,633],{"class":141,"line":268},[139,623,624],{"class":159},"  onUrl",[139,626,342],{"class":163},[139,628,346],{"class":345},[139,630,192],{"class":163},[139,632,351],{"class":345},[139,634,354],{"class":163},[139,636,637,640,642,644,647],{"class":141,"line":274},[139,638,639],{"class":163},"    url.lastmod ",[139,641,486],{"class":152},[139,643,560],{"class":159},[139,645,646],{"class":163},"(entry.date); ",[139,648,649],{"class":145},"\u002F\u002F build error\n",[139,651,652],{"class":141,"line":286},[139,653,519],{"class":163},[139,655,656],{"class":141,"line":294},[139,657,207],{"class":163},[15,659,660,661,663,664,667,668,671,672,675,676,679],{},"The reason: the ",[68,662,541],{}," function is ",[39,665,666],{},"copied into the server bundle as source code."," In the module's code, the function is turned into a string with ",[68,669,670],{},"fn.toString()"," and placed in a virtual server file. ",[68,673,674],{},"toIsoDate"," doesn't exist there, so the module checks at build time and throws an error saying outer variables and imports don't exist in the server bundle and should be moved inside the callback. That's why this blog ",[39,677,678],{},"deliberately duplicates"," the same code in both collections.",[681,682,683],"note",{},[102,684,685],{},[15,686,687,688,691,692,695],{},"In this version, ",[68,689,690],{},"asSitemapCollection()"," prints a deprecation warning suggesting ",[68,693,694],{},"defineSitemapSchema()"," inside the collection schema instead. It works for now, but it's something to change when upgrading the module.",[532,697,699],{"id":698},"is-lastmod-required","Is lastmod required?",[15,701,702,703,706,707,709,710,713],{},"No. A sitemap without ",[68,704,705],{},"lastmod"," is fine, and Google reads its URLs as-is. But Google says it uses ",[68,708,705],{}," when recrawling ",[39,711,712],{},"if it's accurate."," This blog has accurate dates in frontmatter, so it includes them. Stamping today's date on every build when nothing changed would do more harm than good.",[95,715],{},[10,717,719],{"id":718},"_2-canonical-the-pages-representative-url","2. Canonical: the page's representative URL",[102,721,722],{},[15,723,724],{},"A canonical tells Google \"when the same content opens at several URLs, this one is the representative.\"",[15,726,727,728,128],{},"On post pages, set the current path as an absolute URL with ",[68,729,730],{},"useHead",[130,732,736],{"className":733,"code":734,"language":735,"meta":135,"style":135},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F pages\u002Fen\u002F[slug]\u002Findex.vue\nuseHead(() => ({\n  title: `${post.value.title} | iambilly`,\n  link: [\n    { rel: \"canonical\", href: `https:\u002F\u002Fiambilly-blog.vercel.app${route.path}` },\n  ],\n  meta: [\n    { name: \"description\", content: post.value.description },\n    { property: \"og:title\", content: post.value.title },\n    { property: \"og:url\", content: `https:\u002F\u002Fiambilly-blog.vercel.app${route.path}` },\n  ],\n}));\n","js",[68,737,738,743,755,781,786,813,818,823,834,845,867,871],{"__ignoreMap":135},[139,739,740],{"class":141,"line":142},[139,741,742],{"class":145},"\u002F\u002F pages\u002Fen\u002F[slug]\u002Findex.vue\n",[139,744,745,747,750,752],{"class":141,"line":149},[139,746,730],{"class":159},[139,748,749],{"class":163},"(() ",[139,751,572],{"class":152},[139,753,754],{"class":163}," ({\n",[139,756,757,760,763,766,768,771,773,776,779],{"class":141,"line":167},[139,758,759],{"class":163},"  title: ",[139,761,762],{"class":173},"`${",[139,764,765],{"class":163},"post",[139,767,128],{"class":173},[139,769,770],{"class":163},"value",[139,772,128],{"class":173},[139,774,775],{"class":163},"title",[139,777,778],{"class":173},"} | iambilly`",[139,780,327],{"class":163},[139,782,783],{"class":141,"line":183},[139,784,785],{"class":163},"  link: [\n",[139,787,788,791,794,797,800,803,805,808,811],{"class":141,"line":204},[139,789,790],{"class":163},"    { rel: ",[139,792,793],{"class":173},"\"canonical\"",[139,795,796],{"class":163},", href: ",[139,798,799],{"class":173},"`https:\u002F\u002Fiambilly-blog.vercel.app${",[139,801,802],{"class":163},"route",[139,804,128],{"class":173},[139,806,807],{"class":163},"path",[139,809,810],{"class":173},"}`",[139,812,309],{"class":163},[139,814,815],{"class":141,"line":268},[139,816,817],{"class":163},"  ],\n",[139,819,820],{"class":141,"line":274},[139,821,822],{"class":163},"  meta: [\n",[139,824,825,828,831],{"class":141,"line":286},[139,826,827],{"class":163},"    { name: ",[139,829,830],{"class":173},"\"description\"",[139,832,833],{"class":163},", content: post.value.description },\n",[139,835,836,839,842],{"class":141,"line":294},[139,837,838],{"class":163},"    { property: ",[139,840,841],{"class":173},"\"og:title\"",[139,843,844],{"class":163},", content: post.value.title },\n",[139,846,847,849,852,855,857,859,861,863,865],{"class":141,"line":312},[139,848,838],{"class":163},[139,850,851],{"class":173},"\"og:url\"",[139,853,854],{"class":163},", content: ",[139,856,799],{"class":173},[139,858,802],{"class":163},[139,860,128],{"class":173},[139,862,807],{"class":163},[139,864,810],{"class":173},[139,866,309],{"class":163},[139,868,869],{"class":141,"line":318},[139,870,817],{"class":163},[139,872,873],{"class":141,"line":330},[139,874,875],{"class":163},"}));\n",[532,877,879],{"id":878},"noindex-follow-for-filter-urls","noindex, follow for filter URLs",[15,881,882,883,886,887,890,891,893,894,897,898,901],{},"The list pages put filters in the URL, like ",[68,884,885],{},"\u002F?tag=nodejs"," or ",[68,888,889],{},"\u002Fen?field=backend",". The canonical was set to ",[68,892,412],{},", but in actual ",[68,895,896],{},"site:"," results, ",[39,899,900],{},"filter URLs were each indexed, so the same list page showed up several times."," A canonical is a \"hint\" to Google, and Google can decide differently.",[15,903,904,905,908,909,912,913],{},"So for filtered views, ",[68,906,907],{},"robots"," becomes ",[68,910,911],{},"noindex, follow",": ",[39,914,915],{},"don't index this, but keep following the post links in it.",[130,917,919],{"className":733,"code":918,"language":735,"meta":135,"style":135},"\u002F\u002F pages\u002Fen\u002Findex.vue\nconst route = useRoute();\nconst isFilteredView = computed(() => [\"field\", \"tag\", \"q\"].some((k) => k in route.query));\n\nuseHead({\n  meta: [\n    { name: \"robots\", content: () => (isFilteredView.value ? \"noindex, follow\" : \"index, follow\") },\n  ],\n});\n",[68,920,921,926,941,998,1002,1008,1012,1046,1050],{"__ignoreMap":135},[139,922,923],{"class":141,"line":142},[139,924,925],{"class":145},"\u002F\u002F pages\u002Fen\u002Findex.vue\n",[139,927,928,930,933,935,938],{"class":141,"line":149},[139,929,557],{"class":152},[139,931,932],{"class":363}," route",[139,934,367],{"class":152},[139,936,937],{"class":159}," useRoute",[139,939,940],{"class":163},"();\n",[139,942,943,945,948,950,953,955,957,960,963,965,968,970,973,976,979,982,985,987,989,992,995],{"class":141,"line":167},[139,944,557],{"class":152},[139,946,947],{"class":363}," isFilteredView",[139,949,367],{"class":152},[139,951,952],{"class":159}," computed",[139,954,749],{"class":163},[139,956,572],{"class":152},[139,958,959],{"class":163}," [",[139,961,962],{"class":173},"\"field\"",[139,964,192],{"class":163},[139,966,967],{"class":173},"\"tag\"",[139,969,192],{"class":163},[139,971,972],{"class":173},"\"q\"",[139,974,975],{"class":163},"].",[139,977,978],{"class":159},"some",[139,980,981],{"class":163},"((",[139,983,984],{"class":345},"k",[139,986,429],{"class":163},[139,988,572],{"class":152},[139,990,991],{"class":163}," k ",[139,993,994],{"class":152},"in",[139,996,997],{"class":163}," route.query));\n",[139,999,1000],{"class":141,"line":183},[139,1001,254],{"emptyLinePlaceholder":253},[139,1003,1004,1006],{"class":141,"line":204},[139,1005,730],{"class":159},[139,1007,164],{"class":163},[139,1009,1010],{"class":141,"line":268},[139,1011,822],{"class":163},[139,1013,1014,1016,1019,1021,1024,1027,1029,1032,1034,1037,1040,1043],{"class":141,"line":274},[139,1015,827],{"class":163},[139,1017,1018],{"class":173},"\"robots\"",[139,1020,192],{"class":163},[139,1022,1023],{"class":159},"content",[139,1025,1026],{"class":163},": () ",[139,1028,572],{"class":152},[139,1030,1031],{"class":163}," (isFilteredView.value ",[139,1033,392],{"class":152},[139,1035,1036],{"class":173}," \"noindex, follow\"",[139,1038,1039],{"class":152}," :",[139,1041,1042],{"class":173}," \"index, follow\"",[139,1044,1045],{"class":163},") },\n",[139,1047,1048],{"class":141,"line":286},[139,1049,817],{"class":163},[139,1051,1052],{"class":141,"line":294},[139,1053,207],{"class":163},[1055,1056,1057],"warning",{},[102,1058,1059],{},[15,1060,1061,1062,1064,1065,1068,1069,1072,1073,1076,1077,1080],{},"There's a limit. This blog prerenders ",[68,1063,412],{}," and ",[68,1066,1067],{},"\u002Fen"," as static HTML, so the HTML the server sends says ",[68,1070,1071],{},"index, follow"," with or without a filter. The ",[68,1074,1075],{},"noindex"," only applies ",[39,1078,1079],{},"after JavaScript runs in the browser."," Google renders pages, and I confirmed in Chromium that it applies, but crawlers that only read the server HTML won't see it.",[95,1082],{},[10,1084,1086],{"id":1085},"_3-hreflang-pair-korean-and-english-posts","3. hreflang: pair Korean and English posts",[102,1088,1089],{},[15,1090,1091,1092],{},"hreflang says \"the other-language version of this post is at that URL.\" It lets Google know that the same content in two languages is ",[39,1093,1094],{},"a translation, not a duplicate.",[15,1096,1097],{},"I made a file listing only the translated pairs.",[130,1099,1101],{"className":733,"code":1100,"language":735,"meta":135,"style":135},"\u002F\u002F constants\u002FenPairs.js\nexport const EN_PAIRS = {\n  \"javascript-event-loop\": \"javascript-event-loop\",\n  \"cors-guide\": \"cors-guide\",\n  \u002F\u002F Korean slug: English slug\n};\nexport const enToKoSlug = (enSlug) => {\n  const entry = Object.entries(EN_PAIRS).find(([, en]) => en === enSlug);\n  return entry ? entry[0] : null;\n};\n",[68,1102,1103,1108,1123,1135,1147,1152,1157,1179,1226,1252],{"__ignoreMap":135},[139,1104,1105],{"class":141,"line":142},[139,1106,1107],{"class":145},"\u002F\u002F constants\u002FenPairs.js\n",[139,1109,1110,1112,1115,1118,1120],{"class":141,"line":149},[139,1111,153],{"class":152},[139,1113,1114],{"class":152}," const",[139,1116,1117],{"class":363}," EN_PAIRS",[139,1119,367],{"class":152},[139,1121,1122],{"class":163}," {\n",[139,1124,1125,1128,1130,1133],{"class":141,"line":167},[139,1126,1127],{"class":173},"  \"javascript-event-loop\"",[139,1129,912],{"class":163},[139,1131,1132],{"class":173},"\"javascript-event-loop\"",[139,1134,327],{"class":163},[139,1136,1137,1140,1142,1145],{"class":141,"line":183},[139,1138,1139],{"class":173},"  \"cors-guide\"",[139,1141,912],{"class":163},[139,1143,1144],{"class":173},"\"cors-guide\"",[139,1146,327],{"class":163},[139,1148,1149],{"class":141,"line":204},[139,1150,1151],{"class":145},"  \u002F\u002F Korean slug: English slug\n",[139,1153,1154],{"class":141,"line":268},[139,1155,1156],{"class":163},"};\n",[139,1158,1159,1161,1163,1166,1168,1170,1173,1175,1177],{"class":141,"line":274},[139,1160,153],{"class":152},[139,1162,1114],{"class":152},[139,1164,1165],{"class":159}," enToKoSlug",[139,1167,367],{"class":152},[139,1169,446],{"class":163},[139,1171,1172],{"class":345},"enSlug",[139,1174,429],{"class":163},[139,1176,572],{"class":152},[139,1178,1122],{"class":163},[139,1180,1181,1184,1187,1189,1192,1195,1197,1200,1203,1206,1209,1212,1215,1217,1220,1223],{"class":141,"line":286},[139,1182,1183],{"class":152},"  const",[139,1185,1186],{"class":363}," entry",[139,1188,367],{"class":152},[139,1190,1191],{"class":163}," Object.",[139,1193,1194],{"class":159},"entries",[139,1196,342],{"class":163},[139,1198,1199],{"class":363},"EN_PAIRS",[139,1201,1202],{"class":163},").",[139,1204,1205],{"class":159},"find",[139,1207,1208],{"class":163},"(([, ",[139,1210,1211],{"class":345},"en",[139,1213,1214],{"class":163},"]) ",[139,1216,572],{"class":152},[139,1218,1219],{"class":163}," en ",[139,1221,1222],{"class":152},"===",[139,1224,1225],{"class":163}," enSlug);\n",[139,1227,1228,1231,1234,1236,1239,1242,1245,1247,1250],{"class":141,"line":294},[139,1229,1230],{"class":152},"  return",[139,1232,1233],{"class":163}," entry ",[139,1235,392],{"class":152},[139,1237,1238],{"class":163}," entry[",[139,1240,1241],{"class":363},"0",[139,1243,1244],{"class":163},"] ",[139,1246,432],{"class":152},[139,1248,1249],{"class":363}," null",[139,1251,234],{"class":163},[139,1253,1254],{"class":141,"line":312},[139,1255,1156],{"class":163},[15,1257,1258,1259,1262],{},"Post pages add three ",[68,1260,1261],{},"alternate"," links only when a pair exists.",[130,1264,1266],{"className":733,"code":1265,"language":735,"meta":135,"style":135},"const koSlug = computed(() => enToKoSlug(route.params.slug));\nconst base = \"https:\u002F\u002Fiambilly-blog.vercel.app\";\n\nuseHead(() => ({\n  link: koSlug.value\n    ? [\n        { rel: \"alternate\", hreflang: \"ko\", href: `${base}\u002Fko\u002F${koSlug.value}` },\n        { rel: \"alternate\", hreflang: \"en\", href: `${base}${route.path}` },\n        { rel: \"alternate\", hreflang: \"x-default\", href: `${base}\u002Fko\u002F${koSlug.value}` },\n      ]\n    : [],\n}));\n",[68,1267,1268,1288,1302,1306,1316,1321,1329,1363,1393,1422,1427,1435],{"__ignoreMap":135},[139,1269,1270,1272,1275,1277,1279,1281,1283,1285],{"class":141,"line":142},[139,1271,557],{"class":152},[139,1273,1274],{"class":363}," koSlug",[139,1276,367],{"class":152},[139,1278,952],{"class":159},[139,1280,749],{"class":163},[139,1282,572],{"class":152},[139,1284,1165],{"class":159},[139,1286,1287],{"class":163},"(route.params.slug));\n",[139,1289,1290,1292,1295,1297,1300],{"class":141,"line":149},[139,1291,557],{"class":152},[139,1293,1294],{"class":363}," base",[139,1296,367],{"class":152},[139,1298,1299],{"class":173}," \"https:\u002F\u002Fiambilly-blog.vercel.app\"",[139,1301,234],{"class":163},[139,1303,1304],{"class":141,"line":167},[139,1305,254],{"emptyLinePlaceholder":253},[139,1307,1308,1310,1312,1314],{"class":141,"line":183},[139,1309,730],{"class":159},[139,1311,749],{"class":163},[139,1313,572],{"class":152},[139,1315,754],{"class":163},[139,1317,1318],{"class":141,"line":204},[139,1319,1320],{"class":163},"  link: koSlug.value\n",[139,1322,1323,1326],{"class":141,"line":268},[139,1324,1325],{"class":152},"    ?",[139,1327,1328],{"class":163}," [\n",[139,1330,1331,1334,1337,1340,1342,1344,1346,1349,1352,1355,1357,1359,1361],{"class":141,"line":274},[139,1332,1333],{"class":163},"        { rel: ",[139,1335,1336],{"class":173},"\"alternate\"",[139,1338,1339],{"class":163},", hreflang: ",[139,1341,324],{"class":173},[139,1343,796],{"class":163},[139,1345,762],{"class":173},[139,1347,1348],{"class":163},"base",[139,1350,1351],{"class":173},"}\u002Fko\u002F${",[139,1353,1354],{"class":163},"koSlug",[139,1356,128],{"class":173},[139,1358,770],{"class":163},[139,1360,810],{"class":173},[139,1362,309],{"class":163},[139,1364,1365,1367,1369,1371,1374,1376,1378,1380,1383,1385,1387,1389,1391],{"class":141,"line":286},[139,1366,1333],{"class":163},[139,1368,1336],{"class":173},[139,1370,1339],{"class":163},[139,1372,1373],{"class":173},"\"en\"",[139,1375,796],{"class":163},[139,1377,762],{"class":173},[139,1379,1348],{"class":163},[139,1381,1382],{"class":173},"}${",[139,1384,802],{"class":163},[139,1386,128],{"class":173},[139,1388,807],{"class":163},[139,1390,810],{"class":173},[139,1392,309],{"class":163},[139,1394,1395,1397,1399,1401,1404,1406,1408,1410,1412,1414,1416,1418,1420],{"class":141,"line":294},[139,1396,1333],{"class":163},[139,1398,1336],{"class":173},[139,1400,1339],{"class":163},[139,1402,1403],{"class":173},"\"x-default\"",[139,1405,796],{"class":163},[139,1407,762],{"class":173},[139,1409,1348],{"class":163},[139,1411,1351],{"class":173},[139,1413,1354],{"class":163},[139,1415,128],{"class":173},[139,1417,770],{"class":163},[139,1419,810],{"class":173},[139,1421,309],{"class":163},[139,1423,1424],{"class":141,"line":312},[139,1425,1426],{"class":163},"      ]\n",[139,1428,1429,1432],{"class":141,"line":318},[139,1430,1431],{"class":152},"    :",[139,1433,1434],{"class":163}," [],\n",[139,1436,1437],{"class":141,"line":330},[139,1438,875],{"class":163},[44,1440,1441,1451],{},[47,1442,1443],{},[50,1444,1445,1448],{},[53,1446,1447],{},"Rule",[53,1449,1450],{},"Why",[60,1452,1453,1464,1475],{},[50,1454,1455,1461],{},[65,1456,1457,1460],{},[39,1458,1459],{},"Both pages"," must point to each other",[65,1462,1463],{},"If only one does, Google may ignore it. The Korean post page has the same three lines",[50,1465,1466,1469],{},[65,1467,1468],{},"Include the page itself",[65,1470,1471,1472],{},"The English post lists its own URL with ",[68,1473,1474],{},"hreflang=\"en\"",[50,1476,1477,1483],{},[65,1478,1479,1482],{},[68,1480,1481],{},"x-default"," is the URL to show when no language matches",[65,1484,1485],{},"This blog uses the Korean post as the default",[15,1487,1488,1490,1491,1494],{},[68,1489,90],{}," reads these tags from prerendered pages and adds ",[68,1492,1493],{},"hreflang"," entries to the sitemap too.",[95,1496],{},[10,1498,1500],{"id":1499},"_4-structured-data-json-ld-dates-in-iso-8601","4. Structured data (JSON-LD): dates in ISO 8601",[102,1502,1503],{},[15,1504,1505],{},"Structured data spells out, in a machine-readable way, \"this page is a blog post, its title is this, it was published on this date.\"",[15,1507,1508,1509,128],{},"Post pages include a ",[68,1510,1511],{},"BlogPosting",[130,1513,1515],{"className":733,"code":1514,"language":735,"meta":135,"style":135},"useHead(() => ({\n  script: [\n    {\n      type: \"application\u002Fld+json\",\n      innerHTML: JSON.stringify({\n        \"@context\": \"https:\u002F\u002Fschema.org\",\n        \"@type\": \"BlogPosting\",\n        headline: post.value.title,\n        description: post.value.description,\n        datePublished: isoDateTime(post.value), \u002F\u002F \"2026-10-14T00:00:00+09:00\"\n        dateModified: isoDateTime(post.value),\n        mainEntityOfPage: `https:\u002F\u002Fiambilly-blog.vercel.app${route.path}`,\n        author: { \"@type\": \"Person\", name: \"iambilly\" },\n      }),\n    },\n  ],\n}));\n",[68,1516,1517,1527,1532,1537,1547,1562,1574,1586,1591,1596,1610,1620,1637,1658,1663,1668,1672],{"__ignoreMap":135},[139,1518,1519,1521,1523,1525],{"class":141,"line":142},[139,1520,730],{"class":159},[139,1522,749],{"class":163},[139,1524,572],{"class":152},[139,1526,754],{"class":163},[139,1528,1529],{"class":141,"line":149},[139,1530,1531],{"class":163},"  script: [\n",[139,1533,1534],{"class":141,"line":167},[139,1535,1536],{"class":163},"    {\n",[139,1538,1539,1542,1545],{"class":141,"line":183},[139,1540,1541],{"class":163},"      type: ",[139,1543,1544],{"class":173},"\"application\u002Fld+json\"",[139,1546,327],{"class":163},[139,1548,1549,1552,1555,1557,1560],{"class":141,"line":204},[139,1550,1551],{"class":163},"      innerHTML: ",[139,1553,1554],{"class":363},"JSON",[139,1556,128],{"class":163},[139,1558,1559],{"class":159},"stringify",[139,1561,164],{"class":163},[139,1563,1564,1567,1569,1572],{"class":141,"line":268},[139,1565,1566],{"class":173},"        \"@context\"",[139,1568,912],{"class":163},[139,1570,1571],{"class":173},"\"https:\u002F\u002Fschema.org\"",[139,1573,327],{"class":163},[139,1575,1576,1579,1581,1584],{"class":141,"line":274},[139,1577,1578],{"class":173},"        \"@type\"",[139,1580,912],{"class":163},[139,1582,1583],{"class":173},"\"BlogPosting\"",[139,1585,327],{"class":163},[139,1587,1588],{"class":141,"line":286},[139,1589,1590],{"class":163},"        headline: post.value.title,\n",[139,1592,1593],{"class":141,"line":294},[139,1594,1595],{"class":163},"        description: post.value.description,\n",[139,1597,1598,1601,1604,1607],{"class":141,"line":312},[139,1599,1600],{"class":163},"        datePublished: ",[139,1602,1603],{"class":159},"isoDateTime",[139,1605,1606],{"class":163},"(post.value), ",[139,1608,1609],{"class":145},"\u002F\u002F \"2026-10-14T00:00:00+09:00\"\n",[139,1611,1612,1615,1617],{"class":141,"line":318},[139,1613,1614],{"class":163},"        dateModified: ",[139,1616,1603],{"class":159},[139,1618,1619],{"class":163},"(post.value),\n",[139,1621,1622,1625,1627,1629,1631,1633,1635],{"class":141,"line":330},[139,1623,1624],{"class":163},"        mainEntityOfPage: ",[139,1626,799],{"class":173},[139,1628,802],{"class":163},[139,1630,128],{"class":173},[139,1632,807],{"class":163},[139,1634,810],{"class":173},[139,1636,327],{"class":163},[139,1638,1639,1642,1645,1647,1650,1653,1656],{"class":141,"line":336},[139,1640,1641],{"class":163},"        author: { ",[139,1643,1644],{"class":173},"\"@type\"",[139,1646,912],{"class":163},[139,1648,1649],{"class":173},"\"Person\"",[139,1651,1652],{"class":163},", name: ",[139,1654,1655],{"class":173},"\"iambilly\"",[139,1657,309],{"class":163},[139,1659,1660],{"class":141,"line":357},[139,1661,1662],{"class":163},"      }),\n",[139,1664,1665],{"class":141,"line":379},[139,1666,1667],{"class":163},"    },\n",[139,1669,1670],{"class":141,"line":440},[139,1671,817],{"class":163},[139,1673,1674],{"class":141,"line":492},[139,1675,875],{"class":163},[15,1677,1678,1679,1682,1683,1686,1687,1690,1691,446,1694,1697,1698,1701],{},"I made a mistake here. At first, the frontmatter date (",[68,1680,1681],{},"2026.10.14",") went into ",[68,1684,1685],{},"datePublished"," ",[39,1688,1689],{},"as-is."," schema.org and Google expect ",[39,1692,1693],{},"ISO 8601",[68,1695,1696],{},"2026-10-14",", or with a time zone, ",[68,1699,1700],{},"2026-10-14T00:00:00+09:00","), so a dot-separated date may not be read as a date. So I added a converter.",[130,1703,1705],{"className":733,"code":1704,"language":735,"meta":135,"style":135},"\u002F\u002F utils\u002Fcontent.js\nexport const isoDate = (post) => {\n  const m = String(post?.meta?.date ?? \"\").trim().match(\u002F^(\\d{4})\\.(\\d{2})\\.(\\d{2})$\u002F);\n  return m ? `${m[1]}-${m[2]}-${m[3]}` : null;\n};\n\n\u002F\u002F without a time zone the meaning is ambiguous, so add Korea time (+09:00)\nexport const isoDateTime = (post) => {\n  const d = isoDate(post);\n  return d ? `${d}T00:00:00+09:00` : undefined;\n};\n",[68,1706,1707,1712,1733,1802,1857,1861,1865,1870,1891,1905,1929],{"__ignoreMap":135},[139,1708,1709],{"class":141,"line":142},[139,1710,1711],{"class":145},"\u002F\u002F utils\u002Fcontent.js\n",[139,1713,1714,1716,1718,1721,1723,1725,1727,1729,1731],{"class":141,"line":149},[139,1715,153],{"class":152},[139,1717,1114],{"class":152},[139,1719,1720],{"class":159}," isoDate",[139,1722,367],{"class":152},[139,1724,446],{"class":163},[139,1726,765],{"class":345},[139,1728,429],{"class":163},[139,1730,572],{"class":152},[139,1732,1122],{"class":163},[139,1734,1735,1737,1740,1742,1744,1747,1749,1751,1753,1755,1757,1760,1762,1764,1766,1768,1770,1772,1775,1777,1779,1781,1783,1785,1787,1789,1791,1793,1795,1798,1800],{"class":141,"line":167},[139,1736,1183],{"class":152},[139,1738,1739],{"class":363}," m",[139,1741,367],{"class":152},[139,1743,395],{"class":159},[139,1745,1746],{"class":163},"(post?.meta?.date ",[139,1748,373],{"class":152},[139,1750,435],{"class":173},[139,1752,1202],{"class":163},[139,1754,401],{"class":159},[139,1756,404],{"class":163},[139,1758,1759],{"class":159},"match",[139,1761,342],{"class":163},[139,1763,412],{"class":173},[139,1765,451],{"class":152},[139,1767,342],{"class":460},[139,1769,454],{"class":363},[139,1771,457],{"class":152},[139,1773,1774],{"class":460},")",[139,1776,416],{"class":415},[139,1778,342],{"class":460},[139,1780,454],{"class":363},[139,1782,466],{"class":152},[139,1784,1774],{"class":460},[139,1786,416],{"class":415},[139,1788,342],{"class":460},[139,1790,454],{"class":363},[139,1792,466],{"class":152},[139,1794,1774],{"class":460},[139,1796,1797],{"class":152},"$",[139,1799,412],{"class":173},[139,1801,599],{"class":163},[139,1803,1804,1806,1809,1811,1814,1817,1820,1823,1826,1829,1831,1833,1836,1838,1840,1842,1844,1847,1849,1851,1853,1855],{"class":141,"line":183},[139,1805,1230],{"class":152},[139,1807,1808],{"class":163}," m ",[139,1810,392],{"class":152},[139,1812,1813],{"class":173}," `${",[139,1815,1816],{"class":163},"m",[139,1818,1819],{"class":173},"[",[139,1821,1822],{"class":363},"1",[139,1824,1825],{"class":173},"]",[139,1827,1828],{"class":173},"}-${",[139,1830,1816],{"class":163},[139,1832,1819],{"class":173},[139,1834,1835],{"class":363},"2",[139,1837,1825],{"class":173},[139,1839,1828],{"class":173},[139,1841,1816],{"class":163},[139,1843,1819],{"class":173},[139,1845,1846],{"class":363},"3",[139,1848,1825],{"class":173},[139,1850,810],{"class":173},[139,1852,1039],{"class":152},[139,1854,1249],{"class":363},[139,1856,234],{"class":163},[139,1858,1859],{"class":141,"line":204},[139,1860,1156],{"class":163},[139,1862,1863],{"class":141,"line":268},[139,1864,254],{"emptyLinePlaceholder":253},[139,1866,1867],{"class":141,"line":274},[139,1868,1869],{"class":145},"\u002F\u002F without a time zone the meaning is ambiguous, so add Korea time (+09:00)\n",[139,1871,1872,1874,1876,1879,1881,1883,1885,1887,1889],{"class":141,"line":286},[139,1873,153],{"class":152},[139,1875,1114],{"class":152},[139,1877,1878],{"class":159}," isoDateTime",[139,1880,367],{"class":152},[139,1882,446],{"class":163},[139,1884,765],{"class":345},[139,1886,429],{"class":163},[139,1888,572],{"class":152},[139,1890,1122],{"class":163},[139,1892,1893,1895,1898,1900,1902],{"class":141,"line":294},[139,1894,1183],{"class":152},[139,1896,1897],{"class":363}," d",[139,1899,367],{"class":152},[139,1901,1720],{"class":159},[139,1903,1904],{"class":163},"(post);\n",[139,1906,1907,1909,1912,1914,1916,1919,1922,1924,1927],{"class":141,"line":312},[139,1908,1230],{"class":152},[139,1910,1911],{"class":163}," d ",[139,1913,392],{"class":152},[139,1915,1813],{"class":173},[139,1917,1918],{"class":163},"d",[139,1920,1921],{"class":173},"}T00:00:00+09:00`",[139,1923,1039],{"class":152},[139,1925,1926],{"class":363}," undefined",[139,1928,234],{"class":163},[139,1930,1931],{"class":141,"line":318},[139,1932,1156],{"class":163},[532,1934,1936],{"id":1935},"website-and-a-site-name-on-the-home-page","WebSite and a site name on the home page",[15,1938,1939,1940,1943,1944,1947,1948,1951],{},"The home page has a ",[68,1941,1942],{},"WebSite"," describing the site itself, and every page has ",[68,1945,1946],{},"og:site_name",". Google results were showing ",[39,1949,1950],{},"\"Vercel\""," as the site name instead of this blog's name, so I filled in the values Google looks at when choosing a site name.",[130,1953,1955],{"className":733,"code":1954,"language":735,"meta":135,"style":135},"\u002F\u002F pages\u002Findex.vue\nuseHead({\n  script: [\n    {\n      type: \"application\u002Fld+json\",\n      innerHTML: JSON.stringify({\n        \"@context\": \"https:\u002F\u002Fschema.org\",\n        \"@type\": \"WebSite\",\n        name: \"iambilly 기술 블로그\",\n        alternateName: [\"iambilly\", \"iambilly blog\"],\n        url: \"https:\u002F\u002Fiambilly-blog.vercel.app\u002F\",\n      }),\n    },\n  ],\n});\n",[68,1956,1957,1962,1968,1972,1976,1984,1996,2006,2017,2027,2042,2052,2056,2060,2064],{"__ignoreMap":135},[139,1958,1959],{"class":141,"line":142},[139,1960,1961],{"class":145},"\u002F\u002F pages\u002Findex.vue\n",[139,1963,1964,1966],{"class":141,"line":149},[139,1965,730],{"class":159},[139,1967,164],{"class":163},[139,1969,1970],{"class":141,"line":167},[139,1971,1531],{"class":163},[139,1973,1974],{"class":141,"line":183},[139,1975,1536],{"class":163},[139,1977,1978,1980,1982],{"class":141,"line":204},[139,1979,1541],{"class":163},[139,1981,1544],{"class":173},[139,1983,327],{"class":163},[139,1985,1986,1988,1990,1992,1994],{"class":141,"line":268},[139,1987,1551],{"class":163},[139,1989,1554],{"class":363},[139,1991,128],{"class":163},[139,1993,1559],{"class":159},[139,1995,164],{"class":163},[139,1997,1998,2000,2002,2004],{"class":141,"line":274},[139,1999,1566],{"class":173},[139,2001,912],{"class":163},[139,2003,1571],{"class":173},[139,2005,327],{"class":163},[139,2007,2008,2010,2012,2015],{"class":141,"line":286},[139,2009,1578],{"class":173},[139,2011,912],{"class":163},[139,2013,2014],{"class":173},"\"WebSite\"",[139,2016,327],{"class":163},[139,2018,2019,2022,2025],{"class":141,"line":294},[139,2020,2021],{"class":163},"        name: ",[139,2023,2024],{"class":173},"\"iambilly 기술 블로그\"",[139,2026,327],{"class":163},[139,2028,2029,2032,2034,2036,2039],{"class":141,"line":312},[139,2030,2031],{"class":163},"        alternateName: [",[139,2033,1655],{"class":173},[139,2035,192],{"class":163},[139,2037,2038],{"class":173},"\"iambilly blog\"",[139,2040,2041],{"class":163},"],\n",[139,2043,2044,2047,2050],{"class":141,"line":318},[139,2045,2046],{"class":163},"        url: ",[139,2048,2049],{"class":173},"\"https:\u002F\u002Fiambilly-blog.vercel.app\u002F\"",[139,2051,327],{"class":163},[139,2053,2054],{"class":141,"line":330},[139,2055,1662],{"class":163},[139,2057,2058],{"class":141,"line":336},[139,2059,1667],{"class":163},[139,2061,2062],{"class":141,"line":357},[139,2063,817],{"class":163},[139,2065,2066],{"class":141,"line":379},[139,2067,207],{"class":163},[15,2069,2070,2071,2074,2075,2078,2079,1064,2082,120,2085,2087,2088,2091],{},"Since 2023, Google ",[39,2072,2073],{},"supports site names for subdomains"," like ",[68,2076,2077],{},"iambilly-blog.vercel.app",", and recommends ",[68,2080,2081],{},"name",[68,2083,2084],{},"alternateName",[68,2086,1942],{}," structured data to signal your preferred name. But Google chooses the name from several signals, so ",[39,2089,2090],{},"it's not guaranteed."," You have to check search results after deploying.",[95,2093],{},[10,2095,2097],{"id":2096},"_5-prerender-every-post-as-static-html","5. Prerender: every post as static HTML",[102,2099,2100],{},[15,2101,2102],{},"Prerendering builds pages into HTML files ahead of time. The server doesn't render on every request, so it's fast, and crawlers are less likely to get an empty page because of a server error.",[130,2104,2106],{"className":132,"code":2105,"language":134,"meta":135,"style":135},"\u002F\u002F nuxt.config.ts\nnitro: {\n  prerender: {\n    crawlLinks: true,                                \u002F\u002F start at \u002F and follow links\n    ignore: [(route: string) => route.includes(\"?\")], \u002F\u002F don't build filter URLs\n    routes: [\"\u002Fen\"],                                 \u002F\u002F add starting points links don't reach\n  },\n},\n",[68,2107,2108,2112,2120,2127,2143,2179,2196,2200],{"__ignoreMap":135},[139,2109,2110],{"class":141,"line":142},[139,2111,146],{"class":145},[139,2113,2114,2117],{"class":141,"line":149},[139,2115,2116],{"class":159},"nitro",[139,2118,2119],{"class":163},": {\n",[139,2121,2122,2125],{"class":141,"line":167},[139,2123,2124],{"class":159},"  prerender",[139,2126,2119],{"class":163},[139,2128,2129,2132,2134,2137,2140],{"class":141,"line":183},[139,2130,2131],{"class":159},"    crawlLinks",[139,2133,912],{"class":163},[139,2135,2136],{"class":363},"true",[139,2138,2139],{"class":163},",                                ",[139,2141,2142],{"class":145},"\u002F\u002F start at \u002F and follow links\n",[139,2144,2145,2148,2151,2153,2155,2158,2160,2162,2165,2168,2170,2173,2176],{"class":141,"line":204},[139,2146,2147],{"class":159},"    ignore",[139,2149,2150],{"class":163},": [(",[139,2152,802],{"class":345},[139,2154,432],{"class":152},[139,2156,2157],{"class":363}," string",[139,2159,429],{"class":163},[139,2161,572],{"class":152},[139,2163,2164],{"class":163}," route.",[139,2166,2167],{"class":159},"includes",[139,2169,342],{"class":163},[139,2171,2172],{"class":173},"\"?\"",[139,2174,2175],{"class":163},")], ",[139,2177,2178],{"class":145},"\u002F\u002F don't build filter URLs\n",[139,2180,2181,2184,2187,2190,2193],{"class":141,"line":268},[139,2182,2183],{"class":159},"    routes",[139,2185,2186],{"class":163},": [",[139,2188,2189],{"class":173},"\"\u002Fen\"",[139,2191,2192],{"class":163},"],                                 ",[139,2194,2195],{"class":145},"\u002F\u002F add starting points links don't reach\n",[139,2197,2198],{"class":141,"line":274},[139,2199,519],{"class":163},[139,2201,2202],{"class":141,"line":286},[139,2203,2204],{"class":163},"},\n",[15,2206,2207,2210,2211,2214,2215,2218],{},[68,2208,2209],{},"crawlLinks"," only builds ",[39,2212,2213],{},"pages reachable through links."," So this setting doubles as a link-structure check. While the \"Load more\" button problem existed, this crawler found only 35 pages. After adding an ",[68,2216,2217],{},"\u002Farchive"," page listing every post as a link, it built all 159 routes. If the page count in the build log is lower than your post count, Google's crawler can't reach those posts through links either.",[15,2220,2221,2222,2225,2226,2229],{},"Without ",[68,2223,2224],{},"ignore",", it follows the list's filter links (",[68,2227,2228],{},"\u002F?tag=...",") and builds dozens of copies of the same list.",[95,2231],{},[10,2233,2235],{"id":2234},"_6-if-you-moved-urls-use-301-redirects","6. If you moved URLs, use 301 redirects",[15,2237,2238,2239,2242,2243,2246,2247,128],{},"When post URLs moved from ",[68,2240,2241],{},"\u002Fblog\u002F..."," to ",[68,2244,2245],{},"\u002Fko\u002F...",", the old URLs got 301 redirects through ",[68,2248,2249],{},"routeRules",[130,2251,2253],{"className":132,"code":2252,"language":134,"meta":135,"style":135},"routeRules: {\n  \"\u002Fblog\": { redirect: { to: \"\u002F\", statusCode: 301 } },\n  \"\u002Fblog\u002F**\": { redirect: { to: \"\u002Fko\u002F**\", statusCode: 301 } },\n},\n",[68,2254,2255,2261,2281,2297],{"__ignoreMap":135},[139,2256,2257,2259],{"class":141,"line":142},[139,2258,2249],{"class":159},[139,2260,2119],{"class":163},[139,2262,2263,2266,2269,2272,2275,2278],{"class":141,"line":149},[139,2264,2265],{"class":173},"  \"\u002Fblog\"",[139,2267,2268],{"class":163},": { redirect: { to: ",[139,2270,2271],{"class":173},"\"\u002F\"",[139,2273,2274],{"class":163},", statusCode: ",[139,2276,2277],{"class":363},"301",[139,2279,2280],{"class":163}," } },\n",[139,2282,2283,2286,2288,2291,2293,2295],{"class":141,"line":167},[139,2284,2285],{"class":173},"  \"\u002Fblog\u002F**\"",[139,2287,2268],{"class":163},[139,2289,2290],{"class":173},"\"\u002Fko\u002F**\"",[139,2292,2274],{"class":163},[139,2294,2277],{"class":363},[139,2296,2280],{"class":163},[139,2298,2299],{"class":141,"line":183},[139,2300,2204],{"class":163},[15,2302,2303],{},"A 301 means \"moved permanently,\" so Google transfers the old URL's indexing to the new one.",[95,2305],{},[10,2307,2309],{"id":2308},"summary-checklist","Summary: checklist",[44,2311,2312,2322],{},[47,2313,2314],{},[50,2315,2316,2319],{},[53,2317,2318],{},"Item",[53,2320,2321],{},"How to check",[60,2323,2324,2335,2343,2353,2360,2368,2376],{},[50,2325,2326,2329],{},[65,2327,2328],{},"Sitemap",[65,2330,2331,2332,2334],{},"Does ",[68,2333,127],{}," have as many URLs as posts, matching the real post URLs?",[50,2336,2337,2340],{},[65,2338,2339],{},"Canonical",[65,2341,2342],{},"Does the page source have a canonical pointing to itself?",[50,2344,2345,2348],{},[65,2346,2347],{},"Filter URLs",[65,2349,2350,2351,392],{},"After rendering a filter URL, is robots ",[68,2352,911],{},[50,2354,2355,2357],{},[65,2356,1493],{},[65,2358,2359],{},"Do the ko\u002Fen pages point to each other?",[50,2361,2362,2365],{},[65,2363,2364],{},"Structured data",[65,2366,2367],{},"Are dates ISO 8601? Check with Google's Rich Results Test",[50,2369,2370,2373],{},[65,2371,2372],{},"Prerender",[65,2374,2375],{},"Is the build log's page count at least the post count?",[50,2377,2378,2381],{},[65,2379,2380],{},"Link structure",[65,2382,2383,2384,2387],{},"Is there an ",[68,2385,2386],{},"\u003Ca href>"," to every post (an all-posts page)?",[15,2389,2390,2391,2394],{},"More important than any single setting was ",[39,2392,2393],{},"checking the actual output after setting it up."," Most of this blog's problems were \"configured, but the result wasn't what I expected\": the sitemap URLs that didn't match real post URLs, the date format, and the indexed filter URLs were all found only by opening the output and looking.",[10,2396,2398],{"id":2397},"references","References",[19,2400,2401,2409,2416,2423,2430,2437,2444],{},[22,2402,2403],{},[25,2404,2408],{"href":2405,"rel":2406},"https:\u002F\u002Fnuxtseo.com\u002Fsitemap\u002Fguides\u002Fcontent",[2407],"nofollow","Nuxt SEO: Sitemap - Nuxt Content integration",[22,2410,2411],{},[25,2412,2415],{"href":2413,"rel":2414},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fcrawling-indexing\u002Fsitemaps\u002Fbuild-sitemap",[2407],"Google Search Central: Build and submit a sitemap",[22,2417,2418],{},[25,2419,2422],{"href":2420,"rel":2421},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fcrawling-indexing\u002Fconsolidate-duplicate-urls",[2407],"Google Search Central: Consolidate duplicate URLs (canonical)",[22,2424,2425],{},[25,2426,2429],{"href":2427,"rel":2428},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fspecialty\u002Finternational\u002Flocalized-versions",[2407],"Google Search Central: Tell Google about localized versions of your page (hreflang)",[22,2431,2432],{},[25,2433,2436],{"href":2434,"rel":2435},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Farticle",[2407],"Google Search Central: Article structured data",[22,2438,2439],{},[25,2440,2443],{"href":2441,"rel":2442},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fsite-names",[2407],"Google Search Central: Site names",[22,2445,2446],{},[25,2447,2450],{"href":2448,"rel":2449},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fblog\u002F2023\u002F07\u002Fsitenames",[2407],"Google Search Central Blog: An update on site names (2023)",[2452,2453,2454],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .snhLl, html code.shiki .snhLl{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":135,"searchDepth":149,"depth":149,"links":2456},[2457,2458,2462,2465,2466,2469,2470,2471,2472],{"id":12,"depth":149,"text":13},{"id":99,"depth":149,"text":100,"children":2459},[2460,2461],{"id":534,"depth":167,"text":535},{"id":698,"depth":167,"text":699},{"id":718,"depth":149,"text":719,"children":2463},[2464],{"id":878,"depth":167,"text":879},{"id":1085,"depth":149,"text":1086},{"id":1499,"depth":149,"text":1500,"children":2467},[2468],{"id":1935,"depth":167,"text":1936},{"id":2096,"depth":149,"text":2097},{"id":2234,"depth":149,"text":2235},{"id":2308,"depth":149,"text":2309},{"id":2397,"depth":149,"text":2398},"The SEO setup actually applied to a Nuxt 3 + Nuxt Content blog, in code: sitemap lastmod, canonical and noindex for filter URLs, ko\u002Fen hreflang, JSON-LD date format, and prerendering.","md",{"date":1681,"field":2476,"tags":2477},"blog",[2478,70],"seo","\u002Fen\u002Fnuxt3-seo-setup",{"title":5,"description":2473},{"loc":2479},"en\u002Fnuxt3-seo-setup","HTvKIUoCM7PpgQaPK8T4_JZJbUSQ4hCtHGBSbqkF26A",1791974049820]