SEO uživatelského webu
SEO uživatelského webu
Section titled “SEO uživatelského webu”Stránka vznikla jako analýza veřejné části webu (resources/views/web2, routy routes/web.php)
ze dne 11. 9. 2026. Většina nálezů priority P0 je od té doby opravená balíčkem
Laravel Head, zbytek je popsaný na konci.
Shrnutí
Section titled “Shrnutí”| Priorita | Nález | Stav |
|---|---|---|
| P0 | Všechny stránky měly stejný titulek Chytrý kvíz |
Opraveno |
| P0 | Chyběla meta description |
Opraveno |
| P0 | Chyběl rel="canonical", filtry tvořily asi 900 duplicit |
Opraveno |
| P0 | Chyběl Open Graph, sdílení na Facebooku bylo prázdné | Opraveno |
| P0 | Neexistuje sitemap.xml |
Zbývá |
| P1 | Žádná strukturovaná data | Opraveno |
| P1 | Tři stránky nemají h1 |
Zbývá |
| P1 | Chybí drobečková navigace a hreflang |
Zbývá |
| P2 | Šest blokujících souborů z CDN v hlavičce | Zbývá |
| P2 | 52 obrázků obchází komponentu <x-image> |
Zbývá |
Jak se hlavička skládá
Section titled “Jak se hlavička skládá”Titulek, popisek, kanonickou adresu, Open Graph i JSON-LD řeší balíček laravel/head.
Šablony do hlavičky nic nepíšou - vykreslí jen direktivu @head:
web2/layouts/app.blade.php- veřejný web,web2/layouts/guest.blade.php- přihlášení, registrace, výsledek platby,web/www/layouts/base.blade.php- starý layout, po kterém jedou chybové stránky.
Hodnoty se skládají ve čtyřech vrstvách. Vyšší vrstva přepisuje nižší po jednotlivých polích, takže když routa nastaví jen titulek, popisek zůstane z výchozích hodnot.
graph LR;
A["Výchozí hodnoty<br/>AppServiceProvider"] --> B["Metadata routy<br/>->withHead()"];
B --> C["Běhové hodnoty<br/>Head:: v controlleru"];
C --> D["Chybové stránky<br/>Head::errors()"];
1. Výchozí hodnoty
Section titled “1. Výchozí hodnoty”AppServiceProvider::bootHead(). Platí pro každou stránku, která vykresluje @head:
- titulek
Chytrý kvíz – pub kvízy po celém Českus příponou| Chytrý kvíz, - popisek webu,
canonical()z adresy requestu bez query stringu,robots: all,- Open Graph (
website,cs_CZ, název webu) a Twitter kartasummary_large_image, - náhledovka pro sdílení z nastavení,
- barva motivu a název aplikace.
2. Metadata routy
Section titled “2. Metadata routy”Stránky, jejichž titulek je známý dopředu, ho mají rovnou u routy. Pro ně není potřeba sahat do controlleru:
Route::get('/pravidla', ['as' => 'rules', 'uses' => 'HomeController@showRules'])->withHead( title: 'Pravidla kvízového večera', description: 'Jak probíhá kvízový večer, kolik hráčů smí mít tým, jak se boduje…',);Stejně se dá metadata pověsit na celou skupinu rout. Takhle je z indexu vyřazená přihlášená část webu, košík i odkazy z e-mailů nad konkrétním tokenem:
Route::withHead(robots: 'noindex, nofollow')->middleware('auth')->group(function () { // …});3. Běhové hodnoty
Section titled “3. Běhové hodnoty”Co závisí na datech, nastaví controller přes fasádu Head. Většina těchto controllerů
má na to vlastní privátní metodu describe(), aby se to nemíchalo s načítáním dat:
Head::title("Kvíz v {$pubName}, {$city} – {$schedule}") ->description(/* … */) ->og(type: OgType::Article) ->when($image !== null, fn ($head) => $head->ogImage($image, alt: $pubName));Kde se co nastavuje
Section titled “Kde se co nastavuje”| Typ stránky | Kde | Poznámka |
|---|---|---|
| Statické stránky, e-shop, ligy | routes/web.php |
->withHead() u routy |
| Přihlášení a registrace | routes/common.php |
podle jména routy |
| Detail kvízu | QuizController |
titulek z podniku a rozvrhu, JSON-LD Event |
| Detail kraje | RegionController |
titulek včetně počtu podniků |
| Speciál | SpecialsController |
JSON-LD Event |
| Novinky | BlogController |
JSON-LD Article, perex z obsahu |
| Výsledky, týmy, rezervace | ResultController, TeamController, ReservationController |
|
| Produkt e-shopu | EshopController |
JSON-LD Product s cenou a dostupností |
| Stránky z page builderu | PageController |
titulek a meta_description z databáze |
| Hlavní stránka | HomeController |
JSON-LD Organization |
| Chybové stránky | AppServiceProvider |
Head::errors(), noindex, follow |
Náhledovka pro sdílení
Section titled “Náhledovka pro sdílení”Obrázek, který se objeví při sdílení na Facebooku nebo v Messengeru, se nastavuje v adminu v Obecná nastavení → Web → Sdílení a vyhledávače. Jsou tam dvě pole:
| Pole | Klíč nastavení | K čemu |
|---|---|---|
| Náhledovka pro sdílení | web.og_image |
og:image a twitter:image na stránkách bez vlastního obrázku |
| Logo do strukturovaných dat | web.organization_logo |
logo značky v JSON-LD Organization |
Prázdné pole neznamená chybějící náhled - použije se obrázek zabalený v repozitáři
(konstanty DEFAULT_OG_IMAGE a DEFAULT_ORGANIZATION_LOGO v App\Settings\FrontpageSettings).
Stránky, které mají vlastní obrázek (kvíz, speciál, článek, produkt), tuto náhledovku
nepoužívají, spadnou na ni jen tehdy, když u nich obrázek chybí.
Strukturovaná data
Section titled “Strukturovaná data”| Typ | Kde | Co z toho je |
|---|---|---|
Event |
detail kvízu (nejbližší termín), speciál | panel akcí ve výsledcích vyhledávání |
Article |
detail článku v novinkách | náhled článku |
Product |
detail produktu e-shopu | cena a dostupnost ve výsledcích |
Organization |
hlavní stránka | znalostní panel značky, odkazy na sítě z nastavení |
Schémata se skládají přes fasádu Schema. Balíček neprojde prázdné hodnoty, takže
se do schématu nedávají pole, která model nemá vyplněná - jinak v neprodukčním
prostředí vyletí výjimka a na produkci se to zaloguje jako varování.
Co zbývá
Section titled “Co zbývá”Sitemapa P0
Section titled “Sitemapa ”V public/ sitemapa není, žádná routa ji negeneruje a robots.txt na ni neodkazuje.
Obsah je z velké části dynamický - 526 kvízů, 1 224 podniků, 26 krajů, speciály
a novinky. Bez sitemapy je Google objeví jen procházením odkazů.
Oprava: generovaná routa /sitemap.xml, ideálně s indexem a dílčími sitemapami
pro kvízy, kraje, speciály a novinky. Do robots.txt přidat řádek Sitemap:.
Balíček spatie/laravel-sitemap v projektu není, ale samotný XML výstup z Blade
šablony je pár řádků - nová závislost není nutná.
Tři stránky nemají h1 P1
Section titled “Tři stránky nemají h1 ”| Stránka | Počet h1 |
|---|---|
/pro-podniky |
0 |
/firemni-akce |
0 |
/poprve-na-kvizu |
0 |
Všechny tři skládají obsah přes web2/components/page-blocks.blade.php, který vykresluje
nadpisy bloků výhradně jako h2. Stránka tak začíná rovnou druhou úrovní. Jde přitom
o obchodně nejcennější stránky webu.
Oprava: page builder potřebuje u prvního nadpisového bloku volbu úrovně, nebo musí
stránka nad bloky vykreslit h1 z názvu stránky.
Latentní riziko dvou h1 P1
Section titled “Latentní riziko dvou h1 ”Komponenta web2/components/map-section.blade.php má v režimu layout === 'side'
nadpis <h1 id="map-heading">. Hlavní stránka tento režim nepoužívá, takže se dnes
neprojeví. Jakmile ale někdo mapu použije v režimu side na stránce, která už h1 má,
vzniknou dva.
Oprava: změnit nadpis mapy na h2, nebo přidat vlastnost titleTag po vzoru
komponenty parallax.blade.php.
Drobečková navigace P1
Section titled “Drobečková navigace ”Vizuální drobečky má jen web2/pages/result-detail.blade.php. Web má přitom jasnou
hierarchii Kde hrajeme → kraj → podnik → výsledky, kterou nikde nesděluje.
Oprava: sdílená komponenta drobečků a k ní Schema::breadcrumbs() v controlleru.
JSON-LD je připravené, chybí jen ta komponenta.
Slovenský obsah bez hreflang P1
Section titled “Slovenský obsah bez hreflang ”Web má region slovensko na doméně .cz, značka hreflang nikde není. Balíček na to
má metodu alternates(). Při dnešním rozsahu jde o nízkou prioritu.
Výkon a Core Web Vitals P2
Section titled “Výkon a Core Web Vitals ”Layout web2/layouts/app.blade.php na každé stránce načítá šest souborů ze tří cizích
domén:
| Zdroj | Poznámka |
|---|---|
| Google Fonts - Hanken Grotesk, Outfit, Rubik | dvě proměnná písma v plném rozsahu 100–900, blokuje vykreslení |
cdnjs - Glide.js CSS (2 soubory) |
blokuje vykreslení i na stránkách bez karuselu |
cdn.jsdelivr.net - Glide.js |
bez určené verze, neřízená změna cizího kódu |
cdn.jsdelivr.net - GSAP 3.12.5 + ScrollTrigger |
načítá se i tam, kde se animace nepoužije |
Oprava: písma i knihovny zabalit přes Vite a servírovat z vlastní domény, knihovny načítat jen na stránkách, které je potřebují. Verzi Glide každopádně připnout, i mimo SEO.
Obrázky mimo komponentu P2
Section titled “Obrázky mimo komponentu ”V web2 je 52 přímých značek <img>, které komponentu <x-image> obcházejí:
- 7 nemá atribut
altvůbec, - 19 má
alt=""(u dekorativních obrázků správně, u obsahových ne), - 3 mají
loading, - žádná nemá
widthaheight- přímý zdroj posunů rozvržení (CLS).
Oprava: převést obsahové obrázky na <x-image>, která rozměry, webp i odložené
načítání řeší sama.
Co je v pořádku od začátku
Section titled “Co je v pořádku od začátku”- Adresy jsou české, čitelné a bez identifikátorů:
/kviz/{slug},/region/praha,/specialy/{slug},/novinky/post/{slug}. - Přesměrování při přejmenování rout je trvalé (301). Staré adresy
/blog/*a/team/listtaké míří na nové trvale. - Release stránky mají
X-Robots-Tag: noindex, nofollow. robots.txtblokuje SemrushBot, AhrefsBot, MJ12bot a DotBot, ostatní pouští.- Komponenta
<x-image>umíwebp,srcset,sizes,loading="lazy"i rozměry. lang="cs"je na<html>v obou layoutech.- Formuláře chrání honeypot (
ProtectAgainstSpam), newsletter má i throttle.
Jak výsledek ověřit
Section titled “Jak výsledek ověřit”Testy hlavičky jsou v tests/Feature/SeoHeadTest.php, nastavení náhledovky
v tests/Feature/Settings/SettingsUITest.php:
docker-compose exec fpm php artisan test tests/Feature/SeoHeadTest.phpRuční kontrola libovolné stránky:
# Titulek, popisek, kanonická adresa a Open Graphcurl -sk -H "Host: chytrykviz.local" https://localhost/kde-hrajeme \ | grep -iE '<title>|name="description"|rel="canonical"|og:'
# Strukturovaná datacurl -sk -H "Host: chytrykviz.local" https://localhost/kviz/slavia \ | grep -o '<script type="application/ld+json">[^<]*'
# Počet nadpisů h1 na stránce (musí být přesně 1)curl -sk -H "Host: chytrykviz.local" https://localhost/pro-podniky \ | grep -o '<h1' | wc -lPo nasazení zkontrolovat v Google Search Console sestavu Stránky (pokles duplicit), Vylepšení → Akce (strukturovaná data kvízů) a Core Web Vitals.
