[{"data":1,"prerenderedAt":656},["ShallowReactive",2],{"blog-nuxt-i18n-multilingual-seo-hreflang-hu":3},{"slug":4,"published":5,"minutes":6,"category":7,"tags":8,"keywords":14,"about":25,"sources":34,"cover":50,"og":51,"expertise":52,"locales":53,"lang":56,"title":57,"description":58,"coverAlt":59,"metaTitle":60,"takeaways":61,"faq":67,"toc":83,"blocks":114,"others":450},"nuxt-i18n-multilingual-seo-hreflang","2026-10-16",8,"web",[9,10,11,12,13],"Nuxt","i18n","SEO","hreflang","Multilingual",[15,16,17,18,19,20,21,22,23,24],"nuxt i18n seo","nuxt hreflang","useLocaleHead","nuxt i18n canonical","hreflang x-default","prefix_except_default","multilingual seo nuxt","nuxt i18n sitemap hreflang","hreflang self-referencing","nuxt i18n baseUrl",[26,28,31],{"name":9,"url":27},"https:\u002F\u002Fnuxt.com\u002F",{"name":29,"url":30},"Hreflang","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHreflang",{"name":32,"url":33},"Internationalization and localization","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FInternationalization_and_localization",[35,38,41,44,47],{"title":36,"url":37},"Nuxt i18n docs: SEO (useLocaleHead)","https:\u002F\u002Fi18n.nuxtjs.org\u002Fdocs\u002Fguide\u002Fseo",{"title":39,"url":40},"Nuxt i18n docs: Routing strategies","https:\u002F\u002Fi18n.nuxtjs.org\u002Fdocs\u002Fguide",{"title":42,"url":43},"Google Search Central: Tell Google about localized versions of your page","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fspecialty\u002Finternational\u002Flocalized-versions",{"title":45,"url":46},"Google Search Central: Managing multi-regional and multilingual sites","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fspecialty\u002Finternational\u002Fmanaging-multi-regional-sites",{"title":48,"url":49},"Google Search Central: How to specify a canonical URL","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fcrawling-indexing\u002Fconsolidate-duplicate-urls","\u002Fimages\u002Fblog\u002Fnuxt-i18n-multilingual-seo-hreflang\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fnuxt-i18n-multilingual-seo-hreflang\u002Fog.jpg","vue-nuxt-developer",[54,55,56],"en","de","hu","Többnyelvű SEO Nuxt i18n-nel: URL-stratégia, hreflang, canonical és sitemap","Így állítsd be a Nuxt i18n modult, hogy a keresők minden nyelvet indexeljenek: útvonal-stratégia, useLocaleHead, hreflang, részleges fordítás, sitemap.","Egy oldal három nyelvi változata, angol, német és magyar, hreflang nyilakkal összekötve és egy x-default tartalékkal.","Nuxt i18n SEO: hreflang, canonical, sitemap · Balázs Csorba",[62,63,64,65,66],"Minden nyelvnek legyen saját URL-je; a Google ezt ajánlja a sütik vagy a böngészőbeállítások helyett, a Googlebot pedig nem küld Accept-Language fejlécet.","A @nuxtjs\u002Fi18n-ben minden locale-nál add meg a language értéket és az éles baseUrl-t, a useLocaleHead pedig lang, hreflang, og:locale és canonical tageket ad.","A hreflang-készlet minden változaton azonos legyen, tartalmazza magát az oldalt, teljes URL-eket és érvényes ISO-nyelvkódokat használjon.","A kevesebb nyelven létező oldalaknál csak a valódi fordításokat sorold fel, a lefordítatlan másolatok canonicalja pedig az eredetire mutasson.","Ne irányíts át a böngésző nyelve alapján; adj valódi linkekből álló nyelvválasztót, és a hreflang vigye a keresőket a megfelelő változatra.",[68,71,74,77,80],{"q":69,"a":70},"Melyik Nuxt i18n útvonal-stratégia a legjobb SEO-hoz?","A prefix_except_default és a prefix is saját URL-t ad minden nyelvnek, ahogy a Google ajánlja. A prefix_except_default az alapértelmezett nyelvet előtag nélkül hagyja. Nyilvános tartalomnál kerüld a no_prefix stratégiát, mert minden nyelv ugyanazon az URL-en osztozik, és a keresők csak az egyiket tudják indexelni.",{"q":72,"a":73},"A useLocaleHead automatikusan előállítja a hreflangot?","Igen. Visszaadja a html elem lang attribútumát, minden beállított locale-hoz egy hreflang-os alternate linket, az og:locale tageket és egy canonical linket. Ehhez minden locale-nak kell egy language érték, a modulnak pedig baseUrl, hogy az URL-ek teljesek legyenek.",{"q":75,"a":76},"Kell x-default?","Ajánlott. A Google az x-default változatot azoknak a felhasználóknak mutatja, akiknek a nyelve egyik változathoz sem illik. A legtöbb oldalon az alapértelmezett nyelvű változatra vagy egy nyelvválasztó oldalra mutat.",{"q":78,"a":79},"A hreflang a HTML headbe vagy a sitemapbe kerüljön?","Bármelyikbe. A Google egyenértékűnek tekinti a HTML-tageket, a HTTP-fejléceket és a sitemap-bejegyzéseket, és szerinte több módszer együtt nem ad pluszt. Azt válaszd, amelyiket helyesen tudod tartani; egy adatforrásból generált statikus oldalnál mindkettő jöhet ugyanabból a kódból.",{"q":81,"a":82},"Használja a Google a hreflangot az oldal nyelvének felismerésére?","Nem. A Google szerint a nyelvet saját algoritmusaival a tartalomból ismeri fel; a hreflang csak azt mondja meg, mely URL-ek ugyanannak az oldalnak a lokalizált változatai. Egy angol szöveggel teli „német” oldal a Google számára attól még angol.",[84,87,90,93,96,99,102,105,108,111],{"id":85,"title":86},"url-strategy","Milyen URL-stratégiát használjon egy többnyelvű Nuxt-oldal?",{"id":88,"title":89},"configure-nuxt-i18n","Hogyan konfiguráld a @nuxtjs\u002Fi18n modult SEO-hoz?",{"id":91,"title":92},"use-locale-head","Mit tesz a useLocaleHead az oldal headjébe?",{"id":94,"title":95},"hreflang-rules","Mely hreflang-szabályokat rontják el gyakran?",{"id":97,"title":98},"partial-translations","Mi a helyzet a csak egyes nyelveken létező oldalakkal?",{"id":100,"title":101},"sitemaps","A hreflang a sitemapbe is kerüljön?",{"id":103,"title":104},"no-automatic-redirects","Miért ne irányítsd át a látogatókat a böngésző nyelve szerint?",{"id":106,"title":107},"beyond-the-head","Mit kell még lefordítani a többnyelvű SEO-hoz?",{"id":109,"title":110},"test-it","Hogyan ellenőrizd a beállítást?",{"id":112,"title":113},"sources","Források",[115,119,122,125,137,183,186,187,209,211,212,246,248,278,279,288,294,336,342,343,354,356,359,360,363,365,366,373,380,381,417,425,426,429,431,432],{"type":116,"content":117},"paragraph",[118],"Egy többnyelvű Nuxt-oldal a látogatóknak tökéletesnek tűnhet, a keresőmotorok számára mégis félig láthatatlan lehet: a német oldalak nincsenek indexelve, a Google a magyar keresőknek az angol oldalt mutatja, vagy két nyelvi változat versenyez ugyanarra a keresésre. Az ok szinte mindig az oldal headjében van: hiányzó vagy ellentmondásos hreflang linkek, rossz nyelvre mutató canonical, vagy olyan URL-stratégia, amely sütik mögé rejti a nyelveket.",{"type":116,"content":120},[121],"A cikk egy olyan @nuxtjs\u002Fi18n beállítást mutat be, amelyet a keresők értenek, ezt az oldalt használva példaként: statikusan generálódik angolul, németül és magyarul. Szó lesz az útvonal-stratégiáról, a useLocaleHead-ről, a Google dokumentációjának hreflang-szabályairól, a csak egyes nyelveken létező oldalakról, a sitemapekről, és arról, miért árt az automatikus nyelvi átirányítás.",{"type":123,"level":124,"id":85,"text":86},"heading",2,{"type":116,"content":126},[127,128,132,133,136],"A Google ",{"tag":129,"href":46,"children":130},"a",[131],"többnyelvű oldalakra"," vonatkozó ajánlása egyértelmű: minden nyelvi változatnak legyen saját URL-je, sütik vagy böngészőbeállítások helyett. Ha egy URL-en a nyelv szerint változik a tartalom, a Google nem biztos, hogy minden változatot megtalál, mert a Googlebot általában az USA-ból, Accept-Language fejléc nélkül kér le oldalakat. A Nuxt i18n négy ",{"tag":129,"href":40,"children":134},[135],"útvonal-stratégiát"," kínál:",{"type":138,"head":139,"rows":146},"table",[140,142,144],[141],"Stratégia",[143],"URL-ek",[145],"SEO-szempontból",[147,156,165,174],[148,152,154],[149],{"tag":150,"children":151},"code",[20],[153],"\u002Fblog, \u002Fde\u002Fblog, \u002Fhu\u002Fblog",[155],"Jó alapértelmezés; a fő nyelv tiszta URL-eket kap",[157,161,163],[158],{"tag":150,"children":159},[160],"prefix",[162],"\u002Fen\u002Fblog, \u002Fde\u002Fblog, \u002Fhu\u002Fblog",[164],"Jó; szimmetrikus, a gyökérhez átirányítás vagy nyelvválasztó kell",[166,170,172],[167],{"tag":150,"children":168},[169],"prefix_and_default",[171],"\u002Fblog and \u002Fen\u002Fblog, \u002Fde\u002Fblog",[173],"Működik, de duplikátumokat hoz létre; a canonical az előtag nélküli URL-re mutat",[175,179,181],[176],{"tag":150,"children":177},[178],"no_prefix",[180],"\u002Fblog for all",[182],"Nyilvános tartalomnál kerüld; egy URL csak egy nyelven indexelhető",{"type":116,"content":184},[185],"Az egy domainen belüli alkönyvtárak üzemeltetése is a legolcsóbb. Külön országos domain akkor indokolt, ha az üzlet országonként valóban különálló; egy második nyelv kiszolgálásához nem kell.",{"type":123,"level":124,"id":88,"text":89},{"type":116,"content":188},[189,190,193,194,197,198,200,201,204,205,208],"Két beállítás kell ahhoz, hogy az ",{"tag":129,"href":37,"children":191},[192],"SEO-funkciók"," helyes kimenetet adjanak: minden locale-nak kell egy ",{"tag":150,"children":195},[196],"language"," címke, ebből lesz a ",{"tag":150,"children":199},[12]," értéke és a ",{"tag":150,"children":202},[203],"\u003Chtml lang>",", a ",{"tag":150,"children":206},[207],"baseUrl"," pedig az éles origin legyen, mert a hreflang és a canonical URL-eknek teljesnek kell lenniük. Ennek az oldalnak a konfigurációja:",{"type":150,"code":210},"\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  modules: ['@nuxtjs\u002Fi18n'],\n  i18n: {\n    baseUrl: 'https:\u002F\u002Fbalazscsorba.com',\n    strategy: 'prefix_except_default',\n    defaultLocale: 'en',\n    locales: [\n      { code: 'en', language: 'en-US', name: 'English', file: 'en.json' },\n      { code: 'de', language: 'de-AT', name: 'Deutsch', file: 'de.json' },\n      { code: 'hu', language: 'hu-HU', name: 'Magyar', file: 'hu.json' },\n    ],\n    \u002F\u002F No automatic redirect by browser language; the switcher links to every version.\n    detectBrowserLanguage: false,\n  },\n})\n",{"type":123,"level":124,"id":91,"text":92},{"type":116,"content":213},[214,215,218,219,222,223,226,227,230,231,233,234,237,238,241,242,245],"A ",{"tag":150,"children":216},[217],"useLocaleHead()"," olyan head-metaadatot ad vissza, amelyet a ",{"tag":150,"children":220},[221],"useHead","-nek adsz át: a ",{"tag":150,"children":224},[225],"\u003Chtml>"," ",{"tag":150,"children":228},[229],"lang"," attribútumát, minden beállított locale-hoz egy ",{"tag":150,"children":232},[12],"-os alternate linket, ",{"tag":150,"children":235},[236],"og:locale"," és ",{"tag":150,"children":239},[240],"og:locale:alternate"," metatageket, valamint egy canonical linket. Hívd meg egyszer az ",{"tag":150,"children":243},[244],"app.vue","-ban, így minden oldal megkapja.",{"type":150,"code":247},"\u003C!-- app.vue -->\n\u003Cscript setup lang=\"ts\">\nconst i18nHead = useLocaleHead({ dir: true, seo: true })\n\nuseHead(() => ({\n  htmlAttrs: { lang: i18nHead.value.htmlAttrs?.lang },\n  link: [...(i18nHead.value.link ?? [])],\n  meta: [...(i18nHead.value.meta ?? [])],\n}))\n\u003C\u002Fscript>\n",{"type":116,"content":249},[250,251,254,255,258,259,262,263,237,266,269,270,273,274,277],"Egy ",{"tag":150,"children":252},[253],"\u002Fde\u002Fblog\u002Fpost"," oldalon a renderelt head ekkor tartalmazza a ",{"tag":150,"children":256},[257],"\u003Chtml lang=\"de-AT\">"," elemet, egy canonical linket a német URL-re, és alternate linkeket az ",{"tag":150,"children":260},[261],"en-US",", ",{"tag":150,"children":264},[265],"de-AT",{"tag":150,"children":267},[268],"hu-HU"," értékekre. A modul nyelvcsoportonként egy gyűjtő linket is hozzáad, például ",{"tag":150,"children":271},[272],"hreflang=\"en\"",", valamint egy ",{"tag":150,"children":275},[276],"x-default","-ot. Oldaltípusonként egyszer nézd meg a generált HTML-t; könnyű azt hinni, hogy a head rendben van, csak mert a modul telepítve van.",{"type":123,"level":124,"id":94,"text":95},{"type":280,"attrs":281,"inner":285,"caption":286},"diagram",{"viewBox":282,"role":283,"aria-labelledby":284},"0 0 720 310","img","nhl-t nhl-d","\u003Ctitle id=\"nhl-t\">Egy hreflang-csoport\u003C\u002Ftitle>\u003Cdesc id=\"nhl-d\">Egy oldal három nyelvi változata kölcsönösen hivatkozik egymásra, mindegyik önmagát is felsorolja, és egy x-default link a nem illeszkedő nyelvekhez az angol változatra mutat.\u003C\u002Fdesc>\u003Cdefs>\u003Cmarker id=\"nhl-ah\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\">\u003Cpath d=\"M0 0L10 5L0 10z\" class=\"d-head\" \u002F>\u003C\u002Fmarker>\u003C\u002Fdefs>\u003Crect x=\"270\" y=\"20\" width=\"180\" height=\"64\" rx=\"10\" class=\"d-accent\" \u002F>\u003Ctext x=\"360\" y=\"48\" text-anchor=\"middle\" class=\"d-text\">\u002Fblog\u002Fpost\u003C\u002Ftext>\u003Ctext x=\"360\" y=\"70\" text-anchor=\"middle\" class=\"d-small\">hreflang=\"en\"\u003C\u002Ftext>\u003Crect x=\"60\" y=\"190\" width=\"180\" height=\"64\" rx=\"10\" class=\"d-mint\" \u002F>\u003Ctext x=\"150\" y=\"218\" text-anchor=\"middle\" class=\"d-text\">\u002Fde\u002Fblog\u002Fpost\u003C\u002Ftext>\u003Ctext x=\"150\" y=\"240\" text-anchor=\"middle\" class=\"d-small\">hreflang=\"de\"\u003C\u002Ftext>\u003Crect x=\"480\" y=\"190\" width=\"180\" height=\"64\" rx=\"10\" class=\"d-sky\" \u002F>\u003Ctext x=\"570\" y=\"218\" text-anchor=\"middle\" class=\"d-text\">\u002Fhu\u002Fblog\u002Fpost\u003C\u002Ftext>\u003Ctext x=\"570\" y=\"240\" text-anchor=\"middle\" class=\"d-small\">hreflang=\"hu\"\u003C\u002Ftext>\u003Cpath d=\"M300 86L190 186\" class=\"d-line\" marker-start=\"url(#nhl-ah)\" marker-end=\"url(#nhl-ah)\" \u002F>\u003Cpath d=\"M420 86L530 186\" class=\"d-line\" marker-start=\"url(#nhl-ah)\" marker-end=\"url(#nhl-ah)\" \u002F>\u003Cpath d=\"M244 222H476\" class=\"d-line\" marker-start=\"url(#nhl-ah)\" marker-end=\"url(#nhl-ah)\" \u002F>\u003Crect x=\"560\" y=\"28\" width=\"140\" height=\"48\" rx=\"10\" class=\"d-gold\" \u002F>\u003Ctext x=\"630\" y=\"57\" text-anchor=\"middle\" class=\"d-small\">x-default\u003C\u002Ftext>\u003Cpath d=\"M558 52H454\" class=\"d-line-accent\" marker-end=\"url(#nhl-ah)\" \u002F>\u003Ctext x=\"360\" y=\"296\" text-anchor=\"middle\" class=\"d-label\">Minden változat mind a hármat és az x-defaultot felsorolja, önmagát is.\u003C\u002Ftext>",[287],"A hreflang csak zárt készletként működik: ha a német oldal nem hivatkozik vissza, a Google figyelmen kívül hagyhatja a párt.",{"type":116,"content":289},[127,290,293],{"tag":129,"href":43,"children":291},[292],"hreflang-dokumentációja"," rövid, és az auditokban talált hibák többsége ezek közül sért meg egyet:",{"type":295,"items":296},"list",[297,303,311,323,331],[298,302],{"tag":299,"children":300},"strong",[301],"Az oldal önmagát is sorolja fel."," Minden változat változatonként egy linket tartalmaz, önmagát is, és a készlet minden változaton azonos.",[304,307,308,310],{"tag":299,"children":305},[306],"Teljes URL-eket használj",", protokollal és hoszttal. A relatív href többnyire hiányzó ",{"tag":150,"children":309},[207]," következménye.",[312,315,316,318,319,322],{"tag":299,"children":313},[314],"Érvényes kódokat használj:"," ISO 639-1 szerinti nyelvet, opcionálisan ISO 3166-1 szerinti régióval (",{"tag":150,"children":317},[265],"). A régió önmagában nem működik, az olyan kódok pedig, mint az ",{"tag":150,"children":320},[321],"es-419",", nem támogatottak.",[324,330],{"tag":299,"children":325},[326,327,329],"Adj hozzá ",{"tag":150,"children":328},[276],"-ot"," azoknak a felhasználóknak, akiknek a nyelve egyik változathoz sem illik; ez általában az alapértelmezett nyelvű oldal.",[332,335],{"tag":299,"children":333},[334],"A canonical minden nyelvi változaton önmagára mutasson."," Egy német oldal, amelynek canonicalja az angolra mutat, azt üzeni a Google-nek, hogy hagyja el a németet.",{"type":116,"content":337},[338,339,341],"Még egy fontos tény: a Google szerint sem a hreflangot, sem a ",{"tag":150,"children":340},[229]," attribútumot nem használja az oldal nyelvének felismerésére, hanem a tartalomból ismeri fel. Egy lefordított sablon lefordítatlan szövegtörzzsel továbbra is angol oldal.",{"type":123,"level":124,"id":97,"text":98},{"type":116,"content":344},[214,345,347,348,350,351,353],{"tag":150,"children":346},[20]," stratégiával a Nuxt minden locale-hoz útvonalat generál, így egy csak angol cikk is megjelenik a ",{"tag":150,"children":349},[253]," címen, német navigációval és angol szöveggel. Ha nem nyúlsz hozzá, a useLocaleHead német fordításként hirdeti, a német másolat canonicalja pedig önmagára mutat. Ez az oldal mindkettőt az ",{"tag":150,"children":352},[244],"-ban javítja: a kevesebb nyelven létező oldal bejelenti a nyelveit, a hreflang ekkor csak ezeket sorolja fel, a lefordítatlan másolatok canonicalja pedig az angol eredetire mutat.",{"type":150,"code":355},"\u002F\u002F app.vue (excerpt): pageLocales is set by pages that exist in fewer languages\nconst headLinks = computed(() => {\n  const links = i18nHead.value.link ?? []\n  const only = pageLocales.value\n  if (!only || only.path !== route.path) return links\n  return links\n    .filter((l) => l.rel !== 'alternate' || !l.hreflang || l.hreflang === 'x-default'\n      || only.locales.some((c) => l.hreflang.startsWith(c)))\n    .map((l) => (l.rel === 'canonical' && !only.translated\n      ? { ...l, href: `${siteUrl}${switchLocalePath('en')}` }\n      : l))\n})\n",{"type":116,"content":357},[358],"A másik lehetőség, hogy a lefordítatlan útvonalakat egyáltalán nem generálod, hanem 404-et vagy az eredetire mutató átirányítást adsz. Ez a keresőknek tisztább, de rosszabb annak a látogatónak, aki németre állította a felületet, és úgy is akarja hagyni. Mindkettő működik, amíg a head igazat mond arról, mely fordítások léteznek.",{"type":123,"level":124,"id":100,"text":101},{"type":116,"content":361},[362],"A Google három helyen fogadja el a hreflangot – HTML-tagekben, HTTP-fejlécekben és a sitemapben –, egyenértékűnek tekinti őket, és szerinte több módszer együtt nem ad előnyt a keresésben, csak a karbantartást nehezíti. A HTTP-fejléc főleg nem HTML-fájloknál, például PDF-eknél hasznos. Ez az oldal head-tageket és sitemap-bejegyzéseket is kiad, mert mindkettő buildkor ugyanabból a bejegyzésadatból készül, így nem tudnak elcsúszni egymástól. Ha nálad különböző kódutakból jönnek, válassz egyet.",{"type":150,"code":364},"\u003Curl>\n  \u003Cloc>https:\u002F\u002Fbalazscsorba.com\u002Fde\u002Fblog\u002Fagent-loop-explained\u003C\u002Floc>\n  \u003Cxhtml:link rel=\"alternate\" hreflang=\"en\" href=\"https:\u002F\u002Fbalazscsorba.com\u002Fblog\u002Fagent-loop-explained\"\u002F>\n  \u003Cxhtml:link rel=\"alternate\" hreflang=\"de\" href=\"https:\u002F\u002Fbalazscsorba.com\u002Fde\u002Fblog\u002Fagent-loop-explained\"\u002F>\n  \u003Cxhtml:link rel=\"alternate\" hreflang=\"hu\" href=\"https:\u002F\u002Fbalazscsorba.com\u002Fhu\u002Fblog\u002Fagent-loop-explained\"\u002F>\n  \u003Cxhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"https:\u002F\u002Fbalazscsorba.com\u002Fblog\u002Fagent-loop-explained\"\u002F>\n\u003C\u002Furl>\n",{"type":123,"level":124,"id":103,"text":104},{"type":116,"content":367},[368,369,372],"A Google nem javasolja, hogy a feltételezett nyelvük alapján irányítsd át a felhasználókat egy nyelvi változatra, mert ez megakadályozhatja, hogy a felhasználók és a keresők minden változatot elérjenek. A Nuxt i18n ",{"tag":150,"children":370},[371],"detectBrowserLanguage"," beállítása az első látogatáskor átirányíthat; nyilvános oldalon kikapcsolom, és helyette két dologra építek: a hreflangra, amely a találati oldalról közvetlenül a megfelelő változatra viszi a keresőket, és egy valódi linkekből álló nyelvválasztóra, amelyet a crawlerek is követhetnek.",{"type":374,"variant":375,"title":376,"body":377},"callout","tip","Az explicit választást jegyezd meg, ne a találgatást",[378],[379],"A nyelvválasztóban kiválasztott nyelvet nyugodtan eltárolhatod sütiben; ez döntést tükröz. Az Accept-Language fejléc alapján átirányítani viszont találgatás, és a Googlebot ezt a fejlécet egyáltalán nem küldi.",{"type":123,"level":124,"id":106,"text":107},{"type":295,"items":382},[383,388,403,408],[384,387],{"tag":299,"children":385},[386],"Címek és meta-leírások locale-onként."," Ezeket olvassák a keresők a találatok között; egy angol című német oldal kattintásokat veszít.",[389,396,397,402],{"tag":299,"children":390},[391,392,395],"Strukturált adat ",{"tag":150,"children":393},[394],"inLanguage"," értékkel",", amely illik az oldalhoz, hogy az AI-keresés és a bővített találatok a megfelelő változatot idézzék. Erről ",{"tag":398,"to":399,"children":400},"link","\u002Fblog\u002Fgenerative-engine-optimization-audit",[401],"az oldal GEO-auditjában"," írtam.",[404,407],{"tag":299,"children":405},[406],"Képek alt szövege és az Open Graph-képek"," lokalizált szöveggel, ha a kép szavakat tartalmaz.",[409,412,413,416],{"tag":299,"children":410},[411],"A belső linkek maradjanak a nyelven belül."," Mindenhol ",{"tag":150,"children":414},[415],"localePath()","-ot használj, hogy egy német cikk német oldalakra hivatkozzon, és ne terelje vissza a látogatókat az angolra.",{"type":116,"content":418},[419,420,424],"A teljesítmény minden nyelven ugyanannyira számít; ugyanazok a szabályok érvényesek, mint a ",{"tag":398,"to":421,"children":422},"\u002Fblog\u002Fnuxt-core-web-vitals-performance",[423],"Core Web Vitals Nuxthoz"," cikkben, a locale-onként lustán betöltött üzenetfájlok pedig kicsiben tartják a bundle-t.",{"type":123,"level":124,"id":109,"text":110},{"type":116,"content":427},[428],"Kérd le oldaltípusonként és nyelvenként egy oldal generált HTML-jét, és hasonlítsd össze a linkkészleteket; egy rövid szkript elkapja a szokásos hibákat, mielőtt a Search Console hetekkel később tenné.",{"type":150,"code":430},"\u002F\u002F check-hreflang.mjs: every version must list the same set, including itself\nconst pages = ['\u002Fblog\u002Fagent-loop-explained', '\u002Fde\u002Fblog\u002Fagent-loop-explained', '\u002Fhu\u002Fblog\u002Fagent-loop-explained']\nconst base = 'https:\u002F\u002Fbalazscsorba.com'\nconst sets = await Promise.all(pages.map(async (p) => {\n  const html = await (await fetch(base + p)).text()\n  const attr = (tag, name) => tag.match(new RegExp(` ${name}=\"([^\"]+)\"`))?.[1]\n  const tags = html.match(\u002F\u003Clink[^>]*>\u002Fg) ?? []\n  const links = tags.filter((t) => attr(t, 'rel') === 'alternate' && attr(t, 'hreflang'))\n    .map((t) => [attr(t, 'hreflang'), attr(t, 'href')])\n  const canonical = attr(tags.find((t) => attr(t, 'rel') === 'canonical') ?? '', 'href')\n  if (canonical !== base + p) console.warn(`${p}: canonical is ${canonical}`)\n  if (!links.some(([, href]) => href === base + p)) console.warn(`${p}: does not list itself`)\n  return links.map(([lang, href]) => `${lang} ${href}`).sort().join('\\n')\n}))\nconsole.log(new Set(sets).size === 1 ? 'hreflang sets match' : 'hreflang sets differ')\n",{"type":123,"level":124,"id":112,"text":113},{"type":295,"ordered":433,"items":434},true,[435,438,441,444,447],[436],{"tag":129,"href":37,"children":437},[36],[439],{"tag":129,"href":40,"children":440},[39],[442],{"tag":129,"href":43,"children":443},[42],[445],{"tag":129,"href":46,"children":446},[45],[448],{"tag":129,"href":49,"children":449},[48],[451,519,571,610],{"slug":452,"published":5,"minutes":453,"category":7,"tags":454,"keywords":459,"about":472,"sources":479,"cover":513,"og":514,"expertise":52,"locales":515,"lang":56,"title":516,"description":517,"coverAlt":518},"llm-endpoint-abuse-denial-of-wallet",14,[9,455,456,457,458],"LLM security","Rate limiting","OWASP","Cost control",[460,461,462,463,464,465,466,467,468,469,470,471],"denial of wallet llm","owasp llm10 unbounded consumption","rate limit llm api nuxt","nuxt security rate limiter","protect openai api key frontend","llm token budget per user","limit llm cost per user","nitro api rate limiting","cloudflare turnstile nuxt","anthropic spend limit","openai hard spend limit","llm prompt size limit",[473,475,478],{"name":456,"url":474},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRate_limiting",{"name":476,"url":477},"Denial-of-service attack","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDenial-of-service_attack",{"name":9,"url":27},[480,483,486,489,492,495,498,501,504,507,510],{"title":481,"url":482},"OWASP GenAI Security Project: LLM10:2025 Unbounded Consumption","https:\u002F\u002Fgenai.owasp.org\u002Fllmrisk\u002Fllm102025-unbounded-consumption\u002F",{"title":484,"url":485},"Nuxt Security: Rate Limiter middleware","https:\u002F\u002Fnuxt-security.vercel.app\u002Fmiddleware\u002Frate-limiter",{"title":487,"url":488},"Nuxt Security: Request Size Limiter middleware","https:\u002F\u002Fnuxt-security.vercel.app\u002Fmiddleware\u002Frequest-size-limiter",{"title":490,"url":491},"nuxt-auth-utils: README (GitHub)","https:\u002F\u002Fgithub.com\u002Fatinux\u002Fnuxt-auth-utils",{"title":493,"url":494},"h3: request utilities (getRequestIP, readValidatedBody)","https:\u002F\u002Fh3.dev\u002Futils\u002Frequest",{"title":496,"url":497},"Upstash: Ratelimiting algorithms","https:\u002F\u002Fupstash.com\u002Fdocs\u002Fredis\u002Fsdks\u002Fratelimit-ts\u002Falgorithms",{"title":499,"url":500},"Cloudflare Turnstile: Validate the token","https:\u002F\u002Fdevelopers.cloudflare.com\u002Fturnstile\u002Fget-started\u002Fserver-side-validation\u002F",{"title":502,"url":503},"Claude Platform Docs: Rate limits and spend limits","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fapi\u002Frate-limits",{"title":505,"url":506},"Claude Platform Docs: Token counting","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fbuild-with-claude\u002Ftoken-counting",{"title":508,"url":509},"OpenAI API Docs: Spend limits","https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fguides\u002Fspend-limits",{"title":511,"url":512},"OpenAI API Docs: Rate limits","https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fguides\u002Frate-limits","\u002Fimages\u002Fblog\u002Fllm-endpoint-abuse-denial-of-wallet\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fllm-endpoint-abuse-denial-of-wallet\u002Fog.jpg",[54,55,56],"Denial of wallet: nyilvános LLM-végpontok védelme egy Nuxt-alkalmazásban","Visszaélés és elszabaduló költség megállítása egy nyilvános Nuxt LLM-útvonalon: auth, rate limit, promptkorlát, tokenkeret, Turnstile és költéskorlát.","Egy Nuxt chat-végpontra érkező kérések botszűrésen, sessionön, rate limiten, promptkorláton és tokenkereten mennek át, mielőtt elérik a modell API-t.",{"slug":520,"published":521,"minutes":522,"category":7,"tags":523,"keywords":527,"about":535,"sources":542,"cover":564,"og":565,"expertise":566,"locales":567,"lang":56,"title":568,"description":569,"coverAlt":570},"llms-txt-vs-markdown-content-negotiation","2026-10-07",9,[524,525,526,9],"llms.txt","Content negotiation","AI agents",[524,528,529,530,531,532,533,534],"markdown for agents","content negotiation","AI crawlers","generative engine optimization","Accept text\u002Fmarkdown header","llms-full.txt","agent-ready website",[536,539],{"name":537,"url":538},"Markdown","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMarkdown",{"name":540,"url":541},"Hypertext Transfer Protocol","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHypertext_Transfer_Protocol",[543,546,549,552,555,558,561],{"title":544,"url":545},"llmstxt.org: The \u002Fllms.txt file (proposal, version 2)","https:\u002F\u002Fllmstxt.org\u002F",{"title":547,"url":548},"llmstxt.org: Changes from v1 to v2","https:\u002F\u002Fllmstxt.org\u002Fchanges.html",{"title":550,"url":551},"Cloudflare changelog: Markdown for Agents (12 February 2026)","https:\u002F\u002Fdevelopers.cloudflare.com\u002Fchangelog\u002Fpost\u002F2026-02-12-markdown-for-agents\u002F",{"title":553,"url":554},"Cloudflare docs: Markdown for Agents","https:\u002F\u002Fdevelopers.cloudflare.com\u002Ffundamentals\u002Freference\u002Fmarkdown-for-agents\u002F",{"title":556,"url":557},"Ahrefs: 137K sites analyzed, 97% of llms.txt files never get read (June 2026)","https:\u002F\u002Fahrefs.com\u002Fblog\u002Fllmstxt-study\u002F",{"title":559,"url":560},"Checkly: The current state of content negotiation for AI agents (February 2026)","https:\u002F\u002Fwww.checklyhq.com\u002Fblog\u002Fstate-of-ai-agent-content-negotation\u002F",{"title":562,"url":563},"Suganthan: tracking Cloudflare Markdown for Agents on one site","https:\u002F\u002Fsuganthan.com\u002Fblog\u002Fcloudflare-markdown-for-agents\u002F","\u002Fimages\u002Fblog\u002Fllms-txt-vs-markdown-content-negotiation\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fllms-txt-vs-markdown-content-negotiation\u002Fog.jpg","ai-engineer",[54,55,56],"llms.txt vagy Markdown content negotiation: mit töltenek le valójában az ügynökök","Az llms.txt javaslat, a Markdown content negotiation fejléc. Mit töltenek le a MI-ügynökök, mit mutatnak a naplók, és hogyan működik mindez Nuxttal és nginxszel.","Négy rétegből álló halom, amely a MI-ügynököket szolgálja ki: deklarált WebMCP eszközök, minden oldal Markdown-másolata, tárgyalt Markdown válasz, és egy llms.txt index",{"slug":572,"published":573,"minutes":6,"category":7,"tags":574,"keywords":580,"about":588,"sources":597,"cover":604,"og":605,"expertise":52,"locales":606,"lang":56,"title":607,"description":608,"coverAlt":609},"multiplayer-sailing-game-threejs","2026-10-05",[575,576,577,578,579],"three.js","WebGL","GLSL","WebSockets","Game development",[581,582,583,584,585,586,587,577],"three.js game","three.js multiplayer","Gerstner waves","WebGL ocean shader","day night cycle three.js","WebSocket relay","browser game development",[589,592,594],{"name":590,"url":591},"Three.js","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FThree.js",{"name":576,"url":593},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebGL",{"name":595,"url":596},"WebSocket","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebSocket",[598,601],{"title":599,"url":600},"GPU Gems, Chapter 1: Effective Water Simulation from Physical Models","https:\u002F\u002Fdeveloper.nvidia.com\u002Fgpugems\u002Fgpugems\u002Fpart-i-natural-effects\u002Fchapter-1-effective-water-simulation-physical-models",{"title":602,"url":603},"RFC 6455: The WebSocket Protocol","https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc6455","\u002Fimages\u002Fblog\u002Fmultiplayer-sailing-game-threejs\u002Fdusk.webp","\u002Fimages\u002Fblog\u002Fmultiplayer-sailing-game-threejs\u002Fog.jpg",[54,55,56],"Többjátékos 3D vitorlázós játék tiszta three.js-szel","Gerstner-hullámok, amelyeket a GPU és a CPU ugyanúgy számol, egyetlen égbolt-függvény az égre, a vízre és a ködre, ötperces nap-éj ciklus és egy apró WebSocket-relé – a portfólióm játéka mögötti technika.","Sárkányorrú dzsunka világító lampionokkal vitorlázik egy hullámzó öbölben alkonyatkor, két oldalon erdős dombok.",{"slug":611,"published":612,"minutes":522,"category":7,"tags":613,"keywords":619,"about":628,"sources":637,"cover":650,"og":651,"expertise":52,"locales":652,"lang":56,"title":653,"description":654,"coverAlt":655},"home-assistant-ev-charging-energy-manager","2026-09-16",[614,615,616,617,618],"Home Assistant","EPEX Spot","Energy prices","Tesla","Energy management",[620,621,622,623,624,625,626,627],"EPEX Austria prices","spot tariff Austria EV charging","Home Assistant EV charging","cheapest hours charging","day-ahead price Austria","negative electricity prices Austria","Tesla charging spot prices","load management 3 phase",[629,632,634],{"name":630,"url":631},"EPEX SPOT","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEPEX_SPOT",{"name":614,"url":633},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHome_Assistant",{"name":635,"url":636},"Load management","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLoad_management",[638,641,644,647],{"title":639,"url":640},"aWATTar Austria: market data API","https:\u002F\u002Fwww.awattar.at\u002Fservices\u002Fapi",{"title":642,"url":643},"EPEX SPOT: market results","https:\u002F\u002Fwww.epexspot.com\u002Fen\u002Fmarket-results",{"title":645,"url":646},"Home Assistant developer docs: apps","https:\u002F\u002Fdevelopers.home-assistant.io\u002Fdocs\u002Fapps\u002F",{"title":648,"url":649},"Home Assistant: Tesla Fleet integration","https:\u002F\u002Fwww.home-assistant.io\u002Fintegrations\u002Ftesla_fleet\u002F","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fog.jpg",[54,55,56],"Töltés EPEX ausztriai árakon: mennyit spórol a Home Assistant appom","Egy év órás EPEX ár Ausztriára, újrajátszva egy Teslára és egy bojlerre: a legolcsóbb órákban 6,5 ct\u002FkWh a töltés 18,9 helyett, évi közel 590 €, a 20 A-es biztosíték kiégetése nélkül.","Borítókép: három oszlop hasonlítja össze a 18,9, 13,1 és 6,5 ct\u002FkWh-t 18:00-kori, éjszakai és legolcsóbb órás töltésnél.",1792177150760]