Web d3arts.cz běží na WordPressu, na LiteSpeed serveru s využitím LiteSpeed Cache a QuickCloud/CDN. Použitá šablona je Oshine a favicony jsou řešené přes RealFaviconGenerator, který generuje kompletní sadu ikon, manifest a HTML tagy pro moderní prohlížeče i vyhledávače. O generování sitemap.xml se stará Yoast SEO. Problém s faviconem jsme už řešili v článku Favicona WordPress webu není vidět v SERP Google - ale v mém případě bylo řešení trochu komplikovanější, než by se mohlo zdát. Konfigurace webu tedy je:

CMS: WordPress
Server: LiteSpeed
Cache/CDN: LiteSpeed Cache + QuickCloud
Hosting:
BEST-HOSTING.CZ
Favicon plugin: RealFaviconGenerator
SEO plugin: Yoast SEO


Klíčové poznatky

WordPress Site Icon a RealFaviconGenerator se mohou navzájem duplikovat.
Šablona Oshine sama o sobě nebyla hlavní problém, ale generuje hodně vlastního HTML/CSS.
LiteSpeed Cache a QuickCloud výrazně ovlivňují, co reálně dostane Googlebot.
Sitemapu je vhodné cachovat, ale neoptimalizovat/minifikovat.
Crawlerům není vhodné zbytečně vypínat cache přes „Do Not Cache User Agents“.
.webmanifest musí být validní JSON a ideálně se servírovat jako application/manifest+json.

Nejdůležitější zásah u favicon:

Původní problém favicony: duplicitní WordPress Site Icon + RealFaviconGenerator
Řešení favicony: odstranění WordPress Site Icon a ponechání RealFaviconGenerator sady
Původní problém sitemap: pomalé generování / no-cache pro crawlery
Řešení sitemap: cache pro sitemap povolena, optimalizace/minifikace pro sitemap vyloučena


Během kontroly jsme řešili dva problémy, které se na první pohled tvářily odděleně:

  1. Google Search Console hlásila chyby u sitemap, hlavně u post-sitemap.xml.
  2. Favicon se nezobrazovala správně ve výsledcích Google.

Nakonec se ukázalo, že problém nebyl v samotném XML ani v samotných obrázcích favicony. Šlo hlavně o to, jaké hlavičky server vrací crawlerům Googlu, jak funguje cache a zda v HTML nejsou duplicitní instrukce.

Důležité bylo hlavně to, že se na webu potkaly dvě různé favicon konfigurace:

  1. původní WordPress Site Icon
  2. nová sada z RealFaviconGeneratoru

WordPress Site Icon byla nastavená v administraci:
Vzhled → Přizpůsobit → Identita webu → Ikona webu

A právě tato původní WordPress ikona generovala staré favicon tagy, které kolidovaly s novou sadou z RealFaviconGeneratoru. Nebojte se ji v deffaultním nastavení Wordprees v ikoně webu úplně odstranit.


1. Problém se sitemapou v Google Search Console

Google Search Console ukazovala chyby typu u /post-sitemap.xml nebo /portfolio-sitemap.xml, což jsou v mém případě jedny z nejobsžnějších sitemap a hlavní sitemap index je sitemap_index.xml. Na první pohled to vypadalo, že problém může být v samotné sitemapě. Proto jsme začali testovat přímo z terminálu a Windows PowerShellu, co server reálně vrací.

Nelze načíst
Všeobecná chyba HTTP
1 chyba

Hlavní sitemap index byl https://d3arts.cz/sitemap_index.xml

Na první pohled to vypadalo, že problém může být v samotné sitemapě. Proto jsme začali testovat přímo z terminálu a Windows PowerShellu, co server reálně vrací. Podsitemapy v indexu obsahovaly například:


https://d3arts.cz/post-sitemap.xml
https://d3arts.cz/page-sitemap.xml
https://d3arts.cz/product-sitemap.xml
https://d3arts.cz/portfolio-sitemap.xml
https://d3arts.cz/category-sitemap.xml
https://d3arts.cz/video-sitemap.xml
...

Testy ukázaly, že sitemap index i jednotlivé podsitemapy vrací HTTP 200, správný XML content-type a nenulovou velikost. Sitemap tedy nebyla obsahově rozbitá. Problém byl jinde: v rychlosti odpovědi a cache.

Použité diagnostické příkazy pro Windows PowerShell a terminál

Během opravy jsme průběžně ověřovali, co web skutečně vrací crawlerům. Níže jsou příkazy ve dvou verzích: pro Windows PowerShell a pro běžný terminál.

Windows PowerShell

curl.exe --ssl-no-revoke -L -A "Googlebot" "https://d3arts.cz/sitemap_index.xml" -o sitemap-index.xml -w "`nHTTP: %{http_code}`nTime: %{time_total}s`nSize: %{size_download} bytes`nContent-Type: %{content_type}`nFinal URL: %{url_effective}`n"

Terminál

curl -L -A "Googlebot" "https://d3arts.cz/sitemap_index.xml" -o sitemap-index.xml -w "\nHTTP: %{http_code}\nTime: %{time_total}s\nSize: %{size_download} bytes\nContent-Type: %{content_type}\nFinal URL: %{url_effective}\n"

Vypsání všech podsitemap z indexu

Windows PowerShell

[xml]$xml = Get-Content .\sitemap-index.xml
$xml.sitemapindex.sitemap | ForEach-Object { $_.loc }

Terminál

curl -L "https://d3arts.cz/sitemap_index.xml" | grep -oP '(?<=<loc>).*?(?=</loc>)'

Pokud grep -P není dostupný, použít lze jednodušší variantu:

curl -L "https://d3arts.cz/sitemap_index.xml" | sed -n 's:.*<loc>\(.*\)</loc>.*:\1:p'

Kontrola post-sitemap.xml

Obsažná sitemapa týkající se příspěvků wordpress - tedy článků na blogu. Pomalé generování post-sitemap.xml pomocí Yoast SEO. Soubor se původně načítal přibližně přes 5 sekund. To je na XML soubor o velikosti zhruba 141 kB příliš pomalé. To sice znamená, že soubor existoval a byl validně dostupný, ale pro Google Search Console mohl být takový fetch nestabilní. Pokud server nebo CDN v některých momentech odpověděly pomaleji, GSC mohla nahlásit obecnou HTTP chybu. V hlavičkách se objevovalo: x-qc-cache: miss, cache-control: no-cache, no-store, private

Windows PowerShell

curl.exe --ssl-no-revoke -L -A "Googlebot" "https://d3arts.cz/post-sitemap.xml" -o post-sitemap.xml -w "`nHTTP: %{http_code}`nTime: %{time_total}s`nSize: %{size_download} bytes`nContent-Type: %{content_type}`nFinal URL: %{url_effective}`n"

Terminál

curl -L -A "Googlebot" "https://d3arts.cz/post-sitemap.xml" -o /dev/null -w "\nHTTP: %{http_code}\nTime: %{time_total}s\nSize: %{size_download} bytes\nContent-Type: %{content_type}\nFinal URL: %{url_effective}\n"

Důležité hlavičky: x-qc-chache ukazuje, že cache lite speed serveru cachuje sitemapu (což je dobře), protože jinak je doba načítání přes pět vteřin, což Crawler Googlebot vyhodnotí jako nedostupné. To byl jeden z důvodů proč se nedařilo dostat sitemapy do Google Search Console.

HTTP/2 200
content-type: text/xml; charset=UTF-8
x-qc-cache: hit

Kontrola favicon tagů na homepage

Windows PowerShell

curl.exe --ssl-no-revoke -L -A "Googlebot" "https://d3arts.cz/" -o homepage.html
Select-String -Path .\homepage.html -Pattern '<link[^>]+rel="[^"]*icon|<link[^>]+rel="manifest|msapplication-TileImage'

Terminal

curl -L -A "Googlebot" "https://d3arts.cz/" -o homepage.html
grep -Ei '<link[^>]+rel="[^"]*icon|<link[^>]+rel="manifest|msapplication-TileImage' homepage.html

Správně má zůstat jen sada z RealFaviconGeneratoru:

<link rel="icon" type="image/png" href="/wp-content/uploads/fbrfg/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/wp-content/uploads/fbrfg/favicon.svg" />
<link rel="shortcut icon" href="/wp-content/uploads/fbrfg/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/wp-content/uploads/fbrfg/apple-touch-icon.png" />
<link rel="manifest" href="/wp-content/uploads/fbrfg/site.webmanifest" />

Kontrola favicon souborů pro Googlebot-Image

Windows PowerShell

curl.exe --ssl-no-revoke -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/favicon-96x96.png"
curl.exe --ssl-no-revoke -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/favicon.svg"
curl.exe --ssl-no-revoke -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/favicon.ico"
curl.exe --ssl-no-revoke -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/apple-touch-icon.png"

Terminál

curl -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/favicon-96x96.png"
curl -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/favicon.svg"
curl -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/favicon.ico"
curl -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/apple-touch-icon.png"

Správné výsledky:

HTTP/2 200
content-type: image/png
content-type: image/svg+xml
content-type: image/x-icon

Kontrola manifestu

Windows PowerShell

curl.exe --ssl-no-revoke -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/site.webmanifest"

Terminál

curl -L -A "Googlebot-Image" -I "https://d3arts.cz/wp-content/uploads/fbrfg/site.webmanifest"

Správný výsledek:

HTTP/2 200
content-type: application/manifest+json

Vypsání obsahu manifestu

Windows PowerShell

curl.exe --ssl-no-revoke -L "https://d3arts.cz/wp-content/uploads/fbrfg/site.webmanifest"

Terminál

curl -L "https://d3arts.cz/wp-content/uploads/fbrfg/site.webmanifest"

Validace manifestu jako JSON

Tady byla zásadní chyba, protože server servíroval jako content-type: application/octet-stream a ne content-type: application/manifest+json - to změníte buď v .htaccess nebo nastavením serveru - kde je asi radnon zavolat si na podporu.

Oprava .htaccess

<IfModule mod_mime.c>
    AddType application/manifest+json .webmanifest
</IfModule>

Windows PowerShell

Přihlaste se do admin rozhraní: https://server-ip:7800
Virtuals Hosts - Mine Settings
Přidejte nový MINE typ: Suffix: webmanifest a MINE type: application/manifest+json
Uložit a restartovat

pokud toto nezabere, přihlaste se ke svému serveru

curl.exe --ssl-no-revoke -L "https://d3arts.cz/wp-content/uploads/fbrfg/site.webmanifest" -o site.webmanifest
Get-Content .\site.webmanifest | ConvertFrom-Json

Terminal

curl -L "https://d3arts.cz/wp-content/uploads/fbrfg/site.webmanifest" | jq .

Správný výsledek:

{
  "name": "d3arts.cz",
  "short_name": "d3arts",
  "icons": [
    {
      "src": "/wp-content/uploads/fbrfg/web-app-manifest-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "/wp-content/uploads/fbrfg/web-app-manifest-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ],
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "display": "standalone"
}

2. Skutečný problém: cache a pomalé generování sitemap

U post-sitemap.xml se původně objevoval čas načítání kolem pěti sekund. To je na soubor o velikosti zhruba 141 kB zbytečně moc. Z hlaviček bylo vidět, že QuickCloud vrací x-qc-cache: miss, případně že sitemapu server posílal s hlavičkami typu no-cache, no-store, private.

To znamenalo, že sitemapu pokaždé znovu generuje WordPress/Yoast místo toho, aby ji QuickCloud/LiteSpeed rychle servíroval z cache.

V LiteSpeed Cache byly sitemapy původně vyloučené z cachování v záložce Mezipaměť - (4) Excludes / Vyzaření:

/(.)sitemap(.).xml
/(.*)sitemap.xsl
/sitemap_index.xml
/wp-content/plugins/wordpress-seo/css/main-sitemap.xsl

To dávalo smysl z hlediska „vždy čerstvého XML“, ale v praxi to způsobovalo pomalé odpovědi crawlerům. Nastavení jsme proto upravili tak, aby se sitemapy nevylučovaly z cache, ale zároveň se vyloučily z optimalizací/minifikací.

Správná logika Sitemap XML:

neoptimalizovat jako HTML/CSS/JS
cachovat ano
minifikovat ne
lazyload ne

Po úpravě začala post-sitemap.xml vracet z QuickCloudu x-qc-cache: hit a rychlost se zlepšila přibližně z pěti sekund na méně než půl sekundy.

Další skrytý problém: „No cache user agents“

V .htaccess jsme objevili pravidlo, které vyřazovalo z cache různé crawlery. To znamenalo, že crawlerům se vypínala cache. Pro veřejné stránky, sitemapy a favicony to není žádoucí. Googlebot má dostávat rychlou veřejnou verzi webu, stejně jako běžný nepřihlášený návštěvník.

Googlebot-Video
Googlebot-News
Google-InspectionTool
AdsBot-Google
Mediapartners-Google
Bingbot
DuckDuckBot
SeznamBot
Applebot

Tohle nastavení mohlo způsobovat, že Googlebot nedostával rychlou cachovanou verzi, ale pomalejší dynamickou odpověď. Seznam jsme proto z LiteSpeed Cache odstranili. První požadavek po purge byl ještě MISS, což je normální, ale další požadavky už přešly na HIT. Tím se potvrdilo, že crawlerům už server servíruje sitemapu rychle z cache.

Favicon v Google SERPu: problém nebyl v obrázku, ale v duplicitních tazích

Druhý problém byl favicon. V Google výsledcích se nezobrazovala správně, nebo se přestala zobrazovat. Nejdřív jsme ověřili, jestli Googlebot vůbec vidí favicon tagy na homepage.

PowerShell test ukázal, že v HTML byly současně dvě různé sady favicon. Tohle byl hlavní konflikt. Google měl v HTML dvě různé instrukce, jakou faviconu má používat. Staré tagy generoval nativní WordPress přes funkci wp_site_icon(), protože v administraci byla stále nastavená stará „Ikona webu“.

Po odstranění WordPress Site Icon zůstala v HTML jen sada z RealFaviconGeneratoru. Poslední kontrola ukázala, že staré favicon-2 odkazy už ve výpisu nejsou a homepage obsahuje jen novou favicon sadu

RealFaviconGenerator a manifest

Použitý nástroj byl RealFaviconGenerator, který vytvořil složku /wp-content/uploads/fbrfg/

favicon-96x96.png
favicon.svg
favicon.ico
apple-touch-icon.png
site.webmanifest
web-app-manifest-192x192.png
web-app-manifest-512x512.png

Favicon soubory byly dostupné pro Googlebot-Image, vracely HTTP 200 a správné MIME typy, například image/png, image/svg+xml nebo image/x-icon.

U manifestu byl ale ještě jeden technický detail: server ho původně posílal jako:

content-type: application/octet-stream namísto content-type: application/manifest+json

Zde je nutná úprava serverového nastavení.


Máme opraveno a co dál

Teď už není vhodné dál měnit URL ikon, znovu generovat favicony nebo přepisovat sitemap pravidla. Naopak je potřeba nechat Google, aby si nové nastavení znovu načetl.

V Google Search Console je vhodné udělat:

Kontrola URL → https://d3arts.cz/ → Požádat o indexování

Tento krok trochu trvá, proto si vždy zkontrolujte rozkliknutím - položky Indexování stránek, kdy byla stránka naposledy prohledána.

a pro sitemapu nechat odeslanou pouze:

sitemap_index.xml

Jednotlivé podsitemapy jako post-sitemap.xml není potřeba do GSC vkládat ručně.

Pak už zbyde jen čekat co na to Google bot. A neděste se, že vám GSC hlásí, že soubor nejde načíst - pokud jste tam měli již ten soubor předtím pamatuje si starý stav. Bohužel tohle nijak neurychlíte.