Fejlesztés

Kettőskeresztes URL-ek és egyoldalas alkalmazások indexelése

Miért nem indexelhető a #/szolgaltatasok típusú URL, és hogyan javítsd egy React vagy Vue SPA indexelését History API-val és jó státuszkódokkal?

Scheo · · olvasási idő ~10 perc · Szerző: Schmidt Péter

Röviden: A kettőskereszt utáni részt a böngésző nem küldi el a szervernek, ezért a Google a hash alapú SPA összes nézetét jó eséllyel egyetlen oldalnak látja. Ezen a History API alapú útvonalkezelés, a minden útvonalra helyes státuszkódot adó szerver és a valódi href-es belső linkek segíthetnek.
Kulcs tanulságok
  • A #/szolgaltatasok típusú cím nem önálló URL, mert a fragmentet a böngésző nem küldi el a szervernek, és a Google sem kezeli külön oldalként.
  • React, Vue és Angular alatt a hash módot History API alapú routerre kell cserélni, a régi hash címeket pedig kliensoldali szkripttel lehet átirányítani.
  • A szervernek minden létező útvonalra 200-at, minden nem létezőre 404-et kell adnia, a mindenre index.html-t visszaadó beállítás soft 404-hez vezet.
  • A Google csak a valós URL-re mutató href-fel rendelkező a elemeket követi megbízhatóan, az onclick alapú navigációt nem.
  • Az AI-crawlerek egy része a független mérések szerint nem futtat JavaScriptet, ezért az SSR vagy az előre generált HTML a biztonságosabb választás.

Egy egyoldalas alkalmazásként (SPA) épített weboldal a böngészőben gyors és gördülékeny, a keresők és az AI-válaszmotorok szemében viszont könnyen egyetlen oldallá zsugorodik. A leggyakoribb ok egy apró karakter, a kettőskereszt. Ha a szolgáltatásaid a pelda.hu/#/szolgaltatasok, az ajánlatkérő űrlapod a pelda.hu/#/kapcsolat címen él, akkor a Google jó eséllyel mindkettőt ugyanannak a pelda.hu/ oldalnak látja. Ebben a cikkben végigmegyünk azon, miért van ez így, hogyan javítható React, Vue vagy más keretrendszer alatt, és mit kérj a fejlesztődtől egy átadható ellenőrzőlistában.

Kettőskeresztes URL-ek és egyoldalas alkalmazások indexelése
Kettőskeresztes URL-ek és egyoldalas alkalmazások indexelése

A cikkben háromféle jelölést használunk. Hivatalosan igazolt az, ami a Google Search Central dokumentációjában vagy egy szabványban (IETF, WHATWG) szerepel. Saját tapasztalat az, amit weboldal-auditok során rendszeresen látunk. Szakmai feltételezés az, amire jó okunk van, de hivatalos megerősítés nincs rá.

Miért nem indexelhető külön oldalként a #/szolgaltatasok cím?

A kettőskereszt utáni rész (fragment) a böngészőé, a szerver meg sem kapja, és a Google az ilyen címeket alapesetben nem kezeli külön URL-ként. Ezért a /#/szolgaltatasok és a /#/rolunk egyetlen indexelhető oldalnak számít, a főoldalnak.

Hivatalosan igazolt. Az URL-ek általános szintaxisát leíró RFC 3986 szerint a fragment a dokumentumon belüli hivatkozásra szolgál, és a böngésző nem küldi el a szervernek. Ha valaki megnyitja a pelda.hu/#/szolgaltatasok címet, a szerver csak annyit lát, hogy a / útvonalat kérték. A Google JavaScript SEO alapjairól szóló útmutatója kifejezetten leírja, hogy a fragmentekre épülő útvonalakat a Googlebot nem tudja megbízhatóan feloldani, és a History API használatát javasolja helyettük.

Volt egy korábbi kerülőút is, a #! („hashbang”) jelölés és a hozzá tartozó AJAX-feltérképezési séma. A Google ezt 2015-ben elavulttá nyilvánította, 2018 óta pedig nem támogatja. Ha egy régi oldalon még #! címeket látsz, azokra ma már nem érdemes építeni.

Saját tapasztalat. Hash alapú SPA-knál a Search Console jellemzően csak a főoldalt mutatja indexeltként, a belső „oldalak” sehol nem jelennek meg a teljesítményjelentésben. A tartalom tehát létezik, csak nincs saját címe, amelyre egy keresési találat vagy egy AI-válasz hivatkozhatna.

Mi a különbség a hash és a History API alapú útvonalkezelés között?

A hash alapú routing a # után tárolja az útvonalat, a History API alapú pedig valódi, a szerver felé is látható útvonalat (/szolgaltatasok) ír a címsorba, oldalújratöltés nélkül. Keresőoptimalizálás szempontjából csak a második ad minden nézetnek saját, indexelhető URL-t.

A History API a böngésző beépített felülete, amelyet a WHATWG HTML-szabvány ír le. A history.pushState() hívással a kód új bejegyzést tesz az előzményekbe és megváltoztatja a címsort, a popstate esemény pedig jelzi, ha a látogató a vissza gombot használja. A felhasználó ugyanazt a gyors, újratöltés nélküli élményt kapja, a címsorban viszont olyan cím áll, amelyet a szerver is értelmez.

A különbség akkor jön elő, amikor valaki ezt a címet közvetlenül nyitja meg egy Google-találatból, egy megosztott linkből vagy egy AI-válasz forráslistájából. Hash esetén a szerver mindig a főoldalt kapja kérésként. History API esetén a /szolgaltatasok kérés eljut a szerverhez, és ott el kell dönteni, mit válaszol rá. Erről szól a következő két szakasz.

Hogyan állítsd át a routert React, Vue vagy Angular alatt?

A legtöbb keretrendszerben ez egyetlen beállítás, a hash mód helyett a „history” vagy „browser” módot kell választani. A nehezebb rész a szerver és a meglévő linkek rendbetétele.

Az átállásnál a régi hash címeket nem lehet szerveroldali 301-es átirányítással kezelni, mert a szerver nem látja a fragmentet. Erre egy kis kliensoldali szkript kell a főoldalon, amely kiolvassa a location.hash értékét, és a location.replace('/szolgaltatasok') hívással átküldi a látogatót az új címre. Szakmai feltételezés. A Google a JavaScriptes átirányítást is feldolgozza, de mivel a régi hash címek eleve nem voltak külön indexelve, ennek a lépésnek főleg a régi könyvjelzők és a korábban megosztott linkek miatt van értelme.

Milyen válaszkódot kell adnia a szervernek minden útvonalra?

Minden létező útvonalnak 200-as kóddal és lehetőleg kész tartalommal kell válaszolnia, minden nem létezőnek 404-gyel vagy 410-zel, az áthelyezett oldalaknak 301-gyel. A tipikus SPA-beállítás ezzel szemben minden kérésre 200-at és ugyanazt az üres vázat adja vissza.

Saját tapasztalat. A leggyakoribb SPA-szerverbeállítás egyetlen sor, Nginx alatt például try_files $uri $uri/ /index.html;. Ez minden ismeretlen címre az index.html fájlt adja, 200-as kóddal. A /szolgaltatasok így működik, de a /nincs-ilyen-oldal vagy egy elgépelt link is „sikeres” oldalként jön vissza. A Search Console ezeket jellemzően soft 404-ként jelzi, rosszabb esetben a Google üres vagy egymást duplikáló oldalakat próbál feldolgozni.

Hivatalosan igazolt. A Google JavaScript SEO útmutatója két megoldást ír le a kliensoldali útvonalkezelés hibaoldalaira. Az egyik, hogy JavaScripttel átirányítasz egy olyan URL-re, amelyre a szerver valódi 404-et ad. A másik, hogy a hibanézetbe JavaScripttel <meta name="robots" content="noindex"> címkét teszel.

Ennél tisztább, ha a szerver eleve ismeri az útvonalakat. Erre három út van.

  1. Szerveroldali renderelés (SSR). A szerver minden kérésre lefuttatja az alkalmazást, és kész HTML-t küld a megfelelő státuszkóddal. Ha az útvonal nem létezik, vagy a kért terméket törölték, a válasz 404.
  2. Előre generált oldalak (statikus generálás, prerender). A build során minden útvonalhoz külön HTML-fájl készül, például /szolgaltatasok/index.html. Nginx alatt ilyenkor a try_files $uri $uri/index.html =404; és egy error_page 404 /404.html; sor gondoskodik arról, hogy csak a ténylegesen legenerált oldalak kapjanak 200-at.
  3. Útvonal-lista a szerveren. Ha az előző kettő nem fér bele, a szerver legalább egy engedélyezett útvonal-lista alapján döntsön. Ami a listán van, arra mehet az index.html 200-zal, minden másra 404-es státuszkód, akár ugyanazzal az alkalmazásvázzal.

Az áthelyezett vagy megszüntetett oldalaknál ugyanez a logika érvényes. A 301-es átirányítást a szervernek kell kiadnia, a router belső átirányítása ezt nem pótolja teljesen.

Miért kell a belső linkeket valódi href-ként megírni?

A Google csak azokat a linkeket követi megbízhatóan, amelyek <a> elemek, és valós URL-re mutató href attribútumuk van. Egy kattintásra reagáló div vagy egy href nélküli gomb a látogatónak működik, a feltérképező robotnak viszont zsákutca.

Hivatalosan igazolt. A Google feltérképezhető linkekről szóló útmutatója szerint a követhető link egy href attribútummal rendelkező <a> elem, amely feloldható URL-re mutat. A dokumentáció a nem követhető minták között említi a href nélküli, csak onclick eseménnyel működő elemet és a javascript: kezdetű értéket.

A keretrendszerek link-komponensei, a React Router Link és a Vue RouterLink eleme valódi <a href> elemet renderelnek, és közben elkapják a kattintást, hogy ne töltődjön újra az oldal. A gond ott kezdődik, amikor egy gomb onClick eseményéből hívja a kód a navigate() vagy a router.push() függvényt. Saját tapasztalat. Auditok során a főmenü általában rendben van, a kártyák, a „Tovább” gombok és a lapozók viszont gyakran így készülnek, ezért a mélyebb aloldalak (egyes szolgáltatások, blogcikkek) belső link nélkül maradnak.

A # jel a saját helyén továbbra is hasznos, oldalon belüli ugráshoz (/szolgaltatasok#gyik) nyugodtan használhatod. Külön oldalak szétválasztására viszont ne használd.

Elég, ha a Google rendereli a JavaScriptet?

A Google képes JavaScriptet futtatni, de nem minden robot az, és a renderelés nála is késleltetett lehet. Ha a fontos tartalom és a linkek már a szerver által küldött HTML-ben benne vannak, sokkal több rendszer látja őket.

Hivatalosan igazolt. A Googlebot a Chromium aktuális verziójával renderel, a feldolgozás három lépésben (feltérképezés, renderelés, indexelés) zajlik, és a renderelésre váró oldalak sorba kerülnek. A dinamikus renderelést, vagyis a robotoknak külön kiszolgált változatot a Google ma ideiglenes kerülőútnak tekinti, és SSR-t, statikus generálást vagy hidratálást javasol helyette.

Szakmai feltételezés. Az AI-válaszmotorok robotjairól (GPTBot, ClaudeBot, PerplexityBot és társaik) kevés hivatalos információ szól a JavaScript-futtatásról. Független mérések szerint ezek jellemzően a nyers HTML-t dolgozzák fel, és nem futtatják a kliensoldali kódot. Ha ez így van, egy tisztán kliensoldali SPA-ból egy AI-rendszer csak az üres vázat látja. GEO és AEO szempontból ezért az SSR vagy az előre generált HTML a biztonságosabb választás, akkor is, ha a Google-ben a kliensoldali változat elfogadhatóan teljesít.

Minden útvonalnak saját title, meta description és canonical címke kell, ideális esetben már a szerver által küldött HTML-ben. A canonical mindig a valódi, path alapú URL-re mutasson, soha ne hash-es címre.

Hogyan ellenőrizheted, hogy jól működik?

Néhány perces teszttel kiderül, hogy az oldalaid valódi URL-ek-e. Ehhez nem kell fejlesztői tudás, elég egy böngésző és a Search Console.

  1. Kattints a menü egyik pontjára, és nézd meg a címsort. Ha # áll az útvonal előtt, hash alapú a routing.
  2. Másold ki a címet, nyisd meg egy privát ablakban, és frissítsd az oldalt. Ha a főoldal jön be vagy hibát kapsz, a szerver nem ismeri az útvonalat.
  3. Nyiss meg egy biztosan nem létező címet, például /teszt-404-oldal. A fejlesztői eszközök Hálózat (Network) fülén nézd meg a státuszkódot. 404-nek kell lennie, nem 200-nak.
  4. Nézd meg az oldal forrását (Windowson Ctrl+U, Macen Cmd+Option+U). Ha a szöveg és a linkek nincsenek benne, csak egy üres <div id="root"> elem, a tartalom kizárólag JavaScripttel áll elő.
  5. A Search Console URL-ellenőrzés eszközével kérd le az aloldalt, és a feltérképezett oldal megtekintésénél nézd meg a renderelt HTML-t és a képernyőképet.
  6. Futtass egy feltérképező programot (például Screaming Frog) először JavaScript-renderelés nélkül, majd azzal. Ha a talált URL-ek száma között nagy az eltérés, a linkek egy része csak JavaScripttel jön létre.

Mit adj át a fejlesztőnek? Ellenőrzőlista

Az alábbi lista szó szerint továbbítható. Minden pont egy ellenőrizhető állítás, amelyre a fejlesztő igennel vagy nemmel tud felelni.

Szakmai feltételezés. A javítás után az aloldalak indexelése fokozatosan rendeződik, ennek ütemét a Google határozza meg, és az indexelés önmagában semmilyen helyezést nem garantál. Amíg viszont egy szolgáltatásnak nincs saját URL-je, addig nincs mit rangsorolni, és egy AI-válasz sem tud rá forrásként hivatkozni.

Források és további olvasnivalók

Gyakori kérdések

Indexeli a Google a #/ kezdetű címeket külön oldalként?

Alapesetben nem. A kettőskereszt utáni részt a böngésző nem küldi el a szervernek, és a Google az ilyen címeket ugyanannak az oldalnak tekinti, ezért a hash alapú SPA nézetei jó eséllyel a főoldalba olvadnak.

Mit kell átállítani a routerben, hogy indexelhetők legyenek az aloldalak?

Vue Router 4 alatt a createWebHashHistory helyett createWebHistory, React Routerben a HashRouter helyett BrowserRouter vagy createBrowserRouter kell. Ezután a szervert is be kell állítani, hogy minden útvonalra helyes státuszkódot adjon.

Miért baj, ha a szerver minden címre az index.html-t adja vissza?

Mert így a nem létező címek is 200-as kóddal jönnek vissza, amit a Google soft 404-ként kezelhet. A létező útvonalakra 200, a nem létezőkre 404 vagy 410 a helyes válasz.

Átirányíthatók a régi hash címek 301-gyel?

Szerveroldalon nem, mert a szerver nem látja a kettőskereszt utáni részt. A régi hash címeket egy kis kliensoldali szkript tudja az új, path alapú útvonalakra irányítani.

Elég, ha a gombok onclick eseménnyel navigálnak?

A látogatónak működik, a Google viszont csak a valós URL-re mutató href attribútummal rendelkező a elemeket követi megbízhatóan. A navigációs elemeket a keretrendszer link-komponensével vagy valódi a href elemként érdemes megírni.

Látják az AI-válaszmotorok egy kliensoldali SPA tartalmát?

Erről kevés a hivatalos információ, de független mérések szerint több AI-crawler nem futtat JavaScriptet. Ezért a szerveroldali renderelés vagy az előre generált HTML a biztonságosabb megoldás.

Források és további olvasnivalók

Ezen az oldalon a hivatalosan igazolt információt elsődleges forrásokból építjük fel; a saját tapasztalatot és a szakmai becslést mindig külön jelöljük a szövegben.

A cikk szerzője

Schmidt Péter, online marketing szakértő, a Scheo Tanácsadó Kft. alapítója. Székesfehérvárról, országosan dolgozó csapattal végzünk keresőoptimalizálást, AI-láthatóság mérést, Google és Meta hirdetéskezelést, weboldalkészítést. Amit itt leírunk, azt ügyfélmunkában is használjuk. Rólunk bővebben · Szolgáltatásaink

Kapcsolódó olvasnivaló

Kapcsolódó

Ha egy AI már hivatkozik egy oldaladra: URL-változtatás és átalakítás szabályai

Kapcsolódó

Engedd vagy tiltsd az AI-crawlereket? Döntési fa vállalkozásoknak

Helyi szolgáltatás a környékeden

Országosan dolgozunk, online és személyesen. Válaszd ki a városodat, és nézd meg, hogyan segítünk helyben:

Online marketing & AI SEO SzombathelyOnline marketing & AI SEO SzolnokOnline marketing & AI SEO TatabányaOnline marketing & AI SEO KaposvárOnline marketing & AI SEO BékéscsabaOnline marketing & AI SEO Eger

Weboldalkészítés városra bontva

Ha először a honlap kell, itt városonként arról írtunk:

Weboldalkészítés SzekszárdWeboldalkészítés SalgótarjánWeboldalkészítés SzékesfehérvárWeboldalkészítés BudapestWeboldalkészítés VeszprémWeboldalkészítés Dunaújváros

Mind a 20 városunk és az összes szolgáltatás →

15 perces AI-láthatósági gyorselemzés - díjmentesen

Megnézzük három, számodra fontos keresőkérdésnél, hogy megjelenik-e a céged a ChatGPT, a Gemini és a Google AI-válaszaiban, mely versenytársakat ajánlják helyetted, és melyik három területen érdemes először javítani. A weboldaladat előzetesen átnézzük, tehát nem sablonos, automata riportot kapsz.

Adataidat kizárólag a kapcsolatfelvételhez használjuk. Kapcsolat: m@rketinges.hu
💬 Konzultáció