Přejít k obsahu
Pošta Kabinet Vybrat tarif

Logy serveru na hostingu: jak číst access log a error log

Logy serveru na hostingu: jak číst access log a error log

Na sdíleném hostingu se logy serveru v podstatě skládají ze dvou souborů pro každou doménu. Access log zaznamenává každý požadavek na web (IP adresu, čas, URL, stavový kód, user agent), error log jen chyby webového serveru a PHP. V ovládacím panelu oba obvykle otevřete v nastavení domény a k jejich analýze stačí grep, awk a sort: níže najdete hotové příkazy, které během minuty ukážou boty, hádání hesel, chyby 5xx a pomalé stránky.

Jak jsme testovali

10. listopadu 2026 na testovacím stroji: kontejner Ubuntu 24.04.5, nginx 1.24.0 před Apache 2.4.58 (typické nastavení pro hostingové panely), PHP-FPM 8.3.6, MariaDB 10.11.14, WordPress 6.4.3. Skript vygeneroval 9 minut provozu: běžné návštěvníky, dva crawlery, hádání hesel na wp-login.php, útok přes xmlrpc.php, skener hledající .env a .git, pomalou stránku, chybu PHP a 45sekundový výpadek PHP-FPM. Každý příkaz v tomto článku jsme spustili na takto vzniklých lozích, výstupy jsou zkrácené. GoAccess 1.8.1 a logrotate 3.21 pocházejí z repozitáře Ubuntu. Dokumentaci nginx, PHP-FPM a GoAccess jsme ověřili týž den. Jde o chování testovacího serveru, ne o data ze serverů RichHost.

Kde najdete logy svého webu

  • Ovládací panel. Mnoho panelů má u domény náhled access logu a error logu; obvykle ukazuje poslední řádky, což na rychlou kontrolu stačí.
  • Soubory na serveru. Na mnoha serverech s panelem jsou logy domén v /var/log/nginx/domains/ a /var/log/apache2/domains/. Na sdíleném hostingu je jednodušší otevřít je v panelu, nebo soubor stáhnout a spustit stejné příkazy na vlastním počítači. Pokud máte přístup přes SSH, tady je návod, jak se připojit přes SSH z jakéhokoli systému.
  • Log WordPressu. debug.log vznikne až po zapnutí WP_DEBUG_LOG. Náš návod k chybě 500 Internal Server Error ukazuje, jak na to bez prozrazení cest na serveru.
  • Slowlog PHP-FPM. Log pomalých požadavků se nastavuje pro každý pool PHP-FPM zvlášť. Na sdíleném hostingu jde o nastavení serveru: pokud je stránka pomalá bez zjevného důvodu, požádejte podporu, aby se podívala do slowlogu.

Starší dny obvykle leží vedle aktuálního souboru jako .1, .2.gz a tak dále. Proč je to při hledání důležité, vysvětluje část o rotaci.

Access log a error log

Za 9 minut nasbíral access log nginx 4348 řádků. Hádání hesel (563 požadavků), útok na xmlrpc.php (117), skener (90) ani agresivní crawler nezanechaly v žádném error logu jediný řádek: pro webový server jsou to běžné požadavky se stavem 200 a 404. Error log rostl, až když se něco skutečně rozbilo: 11 fatálních chyb PHP a 78 neúspěšných spojení Apache s PHP-FPM během jeho výpadku.

Pravidlo je jednoduché: access log říká kdo, co, kdy, s jakým stavem a jak rychle; error log říká, proč požadavek selhal. Čtěte je společně a párujte podle času. Takto vypadá stejná chyba v obou lozích:

access: 127.0.1.18 - - [10/Nov/2026:14:27:00 +0000] "GET /?s=domen HTTP/1.1" 503 299 ...
error:  [proxy_fcgi:error] [client 127.0.1.18:0] AH01079: failed to make connection to backend: httpd-UDS

Error log nginx zůstal po celý test prázdný, i během 78 odpovědí s kódem 503: vygeneroval je Apache a nginx je jen předal dál. Otevřete error log té vrstvy, která odpověděla.

Formát řádku: které pole je kde

nginx i Apache standardně zapisují ve formátu combined, takže níže uvedené příkazy fungují s oběma. Při dělení podle mezer je IP pole $1, čas $4, URL $7, stav $9, bajty $10. User agenta snáze získáte dělením podle uvozovek: awk -F'"' '{print $6}'.

Když nginx stojí před Apache, oba logy se liší a testovací stroj ukázal jak:

  • Log Apache měl 2215 řádků proti 4348 v nginx: nginx statické soubory (CSS, JS, písma) obslouží sám, takže se k Apache vůbec nedostanou. Provoz a boty počítejte v logu nginx.
  • nginx zapisuje čas, kdy požadavek skončil, Apache čas, kdy začal. Pomalá stránka je v logu nginx ve 14:20:08 a v logu Apache ve 14:20:05.
  • Bez mod_remoteip vidí Apache za nginx na každém řádku 127.0.0.1, takže počítat IP adresy v jeho logu nemá smysl. Na našem dřívějším testovacím stroji přesně takhle selhávaly blokace IP.

Tahák: co hledáte, příkaz, co výsledek znamená

Co hledáte Příkaz Co výsledek znamená
Kdo posílá nejvíc požadavků awk '{print $1}' access.log | sort | uniq -c | sort -rn | head Jedna IP se stovkami požadavků za pár minut: bot nebo útok
Nejžádanější URL awk '{print $7}' access.log | sort | uniq -c | sort -rn | head wp-login.php nebo xmlrpc.php nahoře znamená útok; statické soubory nahoře jsou normální
Rozložení stavových kódů awk '{print $9}' access.log | sort | uniq -c | sort -rn Podíl 404 a 5xx; skok v 5xx znamená výpadek
Kdy nastaly chyby 5xx awk '$9 ~ /^5/ {print substr($4,2,17), $9}' access.log | uniq -c Shluk během jedné minuty: spadla služba; roztroušené: chyba v kódu
Požadavky za minutu awk '{print substr($4,14,5)}' access.log | uniq -c Kdy byla špička; pak se podívejte na IP z té minuty
Za koho se klienti vydávají awk -F'"' '{print $6}' access.log | sort | uniq -c | sort -rn | head Poctiví boti se představí; útoky se často vydávají za Chrome
Hádání hesel grep 'POST /wp-login.php' access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head Stovky POST z jedné IP: brute force; stav 302 znamená úspěšné přihlášení
Útok přes XML-RPC grep 'POST /xmlrpc.php' access.log | awk '{print $1, $9}' | sort | uniq -c | sort -rn Každý požadavek může nést desítky pokusů o heslo
Skenování zranitelností grep -E '\.(env|git)|\.sql|backup' access.log | awk '{print $1, $7, $9}' | sort | uniq -c Hledání tajných údajů a záloh; 404 znamená, že soubor neexistuje
Pomalé stránky příkaz s rt= v části níže Které URL trvají déle než sekundu
Chyby PHP grep -E 'AH01071|PHP (Fatal|Parse) error' error.log | tail -n 3 Soubor a řádek, kde kód selhal
Totéž v archivech zgrep -c 'POST /wp-login.php' access.log* Počet pro každý soubor, včetně komprimovaných

Co ukázalo 9 minut logů

Kdo web zatěžuje

$ awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -4
    613 127.0.2.10
    563 127.0.3.5
    165 127.0.1.10
    156 127.0.1.5

První místo obsadil AhrefsBot: až 206 požadavků za minutu, z toho 278 skončilo kódem 404, protože procházel neexistující URL, a 28,6 MB přenosu proti 15,7 MB u nejaktivnějšího lidského návštěvníka. Druhé místo patřilo tomu, kdo hádal hesla. Následují běžní návštěvníci se 130 až 165 požadavky, včetně statických souborů.

Útočník mezi prohlížeči nevyčníval: jeho řetězec Chrome/79.0.3945.130 na Windows 7 stál v seznamu vedle skutečných a GoAccess započítal všech 563 požadavků jako Chrome. Útok poznáte podle IP a URL, ne podle user agenta. Stejně tak „Googlebot“ v řádku logu nic nedokazuje: Google doporučuje zpětný DNS dotaz příkazem host; název musí končit na googlebot.com, google.com nebo googleusercontent.com a překládat se zpět na stejnou IP.

Brute force na wp-login.php a xmlrpc.php

$ grep 'POST /wp-login.php' access.log | awk '{print substr($4,14,5)}' | uniq -c
     13 14:21
    140 14:22
    141 14:23
    141 14:24
    128 14:25

Všech 563 pokusů dostalo stav 200: po neúspěšném přihlášení WordPress prostě znovu zobrazí formulář. Úspěšné přihlášení vrátilo na testovacím stroji 302 s přesměrováním do nástěnky, takže grep 'POST /wp-login.php' access.log | awk '$9==302' ukáže, zda bylo nějaké heslo uhodnuto. Útok přes XML-RPC je zákeřnější: v logu jen 117 řádků se stavem 200, ale každý požadavek system.multicall nesl 20 pokusů a v odpovědi server 20krát napsal “Incorrect username or password.”. To je 2340 hesel za dvě minuty bez jediného chybového stavu v logu.

Skenování .env a .git

Skener, který se podepisoval jako zgrab, prošel třikrát 30 cest: /.env, /.git/config, /wp-config.php.bak, /backup.zip, /db.sql a další. 84 z 90 požadavků dostalo 404, a to je dobrý výsledek. Každá odpověď ale vážila 56 kB a trvala asi 0,04 s: tyto požadavky procházejí WordPressem, protože soubor neexistuje a pravidla pro přepis je předají index.php. Pokud se skenery objevují denně, vyplatí se je odmítat pravidlem webového serveru.

Chyby 5xx v čase

$ awk '$9 ~ /^5/ {print substr($4,2,17), $9}' access.log | uniq -c
      2 10/Nov/2026:14:22 500
      2 10/Nov/2026:14:23 500
      3 10/Nov/2026:14:24 500
      1 10/Nov/2026:14:25 500
      1 10/Nov/2026:14:26 500
     78 10/Nov/2026:14:27 503
      2 10/Nov/2026:14:28 500

Skrývají se tu dva různé příběhy. Roztroušené chyby 500 přišly všechny z jedné URL, admin-ajax.php?action=rh_export, a ke každé je v logu Apache řádek AH01071 s přesným místem: Call to undefined function rh_export_orders_csv() in .../bench-shop.php:15. 78 odpovědí s kódem 503 během 45 sekund (od 14:27:00 do 14:27:44), všechny s rt=0.000, znamená, že nefungoval celý web: Apache se nemohl spojit s PHP-FPM. Bez Apache vrací nginx ve stejné situaci chybu 502 Bad Gateway. Pokud prohlížeč místo toho ukazuje 508, jde o limit prostředků tarifu, kterému se věnuje článek chyba 508 Resource Limit Is Reached.

Pomalé požadavky: $request_time a slowlog PHP-FPM

Standardní formát combined časy neobsahuje. Abyste je získali, definujte v nginx vlastní formát. Přidali jsme dvě proměnné: $request_time (celkový čas od prvního bajtu požadavku do odeslání odpovědi, v sekundách s přesností na milisekundy) a $upstream_response_time (jak dlouho trval backend):

log_format main_rt '$remote_addr - $remote_user [$time_local] "$request" '
                   '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
                   'rt=$request_time urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main_rt;

Průměrný čas pro každou URL je pak jediný příkaz:

$ awk 'match($0, /rt=[0-9.]+/) {t=substr($0, RSTART+3, RLENGTH-3); n[$7]++; s[$7]+=t}
  END {for (u in n) if (n[u]>=5) printf "%.3f %5d %s\n", s[u]/n[u], n[u], u}' access.log | sort -rn | head -4
3.419    48 /kursy-valyut/
0.095    40 /category/uncategorized/
0.090    52 /post-5/
0.081    49 /

Stránka s kurzy měn trvala 3,5 až 3,7 s, zbytek webu pod 0,1 s. Access log říká, kde je to pomalé, slowlog PHP-FPM říká proč. V konfiguraci poolu stačí dva řádky:

request_slowlog_timeout = 2s
slowlog = /var/log/php-fpm/site-slow.log

Výchozí hodnota je 0, tedy vypnuto. Na testovacím stroji nasbíral slowlog 46 záznamů, jeden na každý úspěšný požadavek na pomalou stránku. Tady je začátek jednoho z nich (celkem 20 řádků zásobníku, výchozí hloubka):

[10-Nov-2026 14:22:55]  [pool cw13] pid 1056
script_filename = /srv/cw13/www/index.php
[0x00007f34d9214010] curl_exec() /srv/cw13/www/wp-includes/Requests/src/Transport/Curl.php:204
[0x00007f34d9213f30] request() /srv/cw13/www/wp-includes/Requests/src/Requests.php:469
...
[0x00007f34d9213b70] wp_remote_get() /srv/cw13/www/wp-content/mu-plugins/bench-shop.php:4
[0x00007f34d9213b00] rh_fetch_exchange_rates() /srv/cw13/www/wp-content/mu-plugins/bench-shop.php:8
[0x00007f34d9213a80] rh_render_rates_widget() /srv/cw13/www/wp-content/mu-plugins/bench-shop.php:12

Zásobník čtěte shora dolů: v tu chvíli PHP čekalo v curl_exec() na externí API, které volala funkce rh_fetch_exchange_rates() v našem pluginu. Oprava patří do kódu, ne na server: kurz na hodinu ukládat do cache a nastavit krátký timeout. Všimněte si, že PHP-FPM zapsalo GET /index.php, a ne URL stránky: s pěknými trvalými odkazy jde ve WordPressu všechno přes index.php, URL proto najdete v access logu podle času. Širší pohled na zpomalení nabízí náš návod, jak diagnostikovat problémy s hostingem.

Rotace logů: proč grep nevidí včerejší útoky

Logy se každý den přejmenují a zkomprimují, jinak by zaplnily disk. Typická konfigurace logrotate:

/var/log/nginx/access.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    postrotate
        [ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
    endscript
}

Po dvou rotacích byly na testovacím stroji access.log, access.log.1 (kvůli delaycompress ještě nezkomprimovaný) a access.log.2.gz: 1 086 129 bajtů logu se zmenšilo na 45 278, tedy 24krát. Teď hledání:

$ grep -c "POST /wp-login.php" access.log*
access.log:2
access.log.1:3
access.log.2.gz:0
$ zgrep -c "POST /wp-login.php" access.log*
access.log:2
access.log.1:3
access.log.2.gz:563

Obyčejný grep pro archiv, ve kterém bylo všech 563 pokusů o přihlášení, tiše vrátil 0. Pro komprimované soubory používejte zgrep a v rourách s awk nejdřív vše spojte: zcat -f access.log* | awk .... Přepínač -f propustí nekomprimované soubory beze změny.

GoAccess: přehled za sekundu

Když potřebujete celkový obraz, a ne jednu odpověď, použijte GoAccess. Pro náš formát s časy:

goaccess access.log --log-format='%h %^[%d:%t %^] "%r" %s %b "%R" "%u" rt=%T %^' \
  --date-format=%d/%b/%Y --time-format=%T -o report.html

Pro standardní log stačí --log-format=COMBINED. Na testovacím stroji se testovací log (4349 řádků) zpracoval za 0,21 s do jediného HTML souboru o velikosti 648 kB: nejčastější URL (wp-login.php na prvním místě, 563 požadavků v průměru po 25 ms), stavové kódy, crawlery jako samostatná skupina, chyby 404 a doby odezvy. Přehled obsahuje IP adresy návštěvníků, proto ho nedávejte do veřejné složky webu.

Když vidíte X, udělejte Y

V logu Co dělat
Stovky POST na wp-login.php z jedné nebo několika IP Omezit pokusy o přihlášení, zapnout dvoufázové ověření, zablokovat IP; zkontrolovat, že žádná nedostala 302
Mnoho POST na xmlrpc.php, a přitom nepoužíváte mobilní aplikace ani Jetpack Zablokovat xmlrpc.php pravidlem webového serveru nebo pluginem
404 na .env, .git, zálohy Ověřit, že takové soubory v kořeni webu opravdu nejsou; časté skeny odmítat pravidlem
Crawler posílá stovky požadavků za minutu Ověřit, zda je pravý; nežádoucího bota omezit v robots.txt nebo zablokovat podle user agenta
Shluk 502 nebo 503 během jedné minuty Otevřít error log pro tu minutu: PHP-FPM nebo backend neodpovídal
Roztroušené 500 na jedné URL Najít AH01071 nebo PHP Fatal error: uvádí soubor a řádek
Jedna stránka trvá sekundy Najít ji ve slowlogu, externí volání ukládat do cache
403 v access logu, nic v error logu Hledat bezpečnostní plugin nebo pravidlo, které odmítá potichu

Jak taková odmítnutí rozlišit, vysvětluje náš návod k chybě 403 Forbidden. Všechny tarify sdíleného hostingu RichHost zahrnují vlastní ovládací panel s asistentem AI a podpora funguje od 10:00 do 20:00 kyjevského času: do požadavku uveďte URL, přesný čas a řádek z logu a příčina se najde mnohem rychleji.

Časté otázky

Proč všechny požadavky v mém logu přicházejí z 127.0.0.1?

Čtete log backendu za proxy: Apache vidí nginx, ne návštěvníka. Použijte log nginx nebo požádejte o předávání skutečné IP (mod_remoteip).

Znamená stav 200 na wp-login.php, že se útočník dostal dovnitř?

Ne. Neúspěšné přihlášení vrací 200 a znovu zobrazí formulář. Úspěšné přihlášení vrací přesměrování 302 a právě to hledejte.

Jak dlouho se logy uchovávají?

Tak dlouho, jak určuje nastavení rotace na serveru: v příkladu výše 14 dní. Pokud vyšetřujete incident, stáhněte potřebné soubory hned, než je rotace smaže.

Proč číst logy, když mám Google Analytics?

Analytics počítá jen prohlížeče, které spustily JavaScript. Boty, útoky, chyby 5xx, požadavky na statické soubory a doby odezvy serveru uvidíte jen v lozích.

Více k tématu

Všechny články
Bezpečnostní audit webu: checklist na hodinu se skutečnými příkazy
Návody a tutoriály k hostingu11.06.2026

Bezpečnostní audit webu: checklist na hodinu se skutečnými příkazy

Jak za hodinu provést audit webu na sdíleném hostingu nebo vlastním VPS: odhalené .git, .env a výpisy databáze, verze WordPressu a pluginů,…

Práva souborů na hostingu: chmod 644, 755, 600 a proč nikdy 777
Návody a tutoriály k hostingu07.05.2026

Práva souborů na hostingu: chmod 644, 755, 600 a proč nikdy 777

Jaká práva nastavit adresářům, souborům a wp-config.php, co mění uživatel PHP, proč je 777 nebezpečné a jak hromadně opravit práva příkazem find.…

Sousedé na sdíleném hostingu: jak ovlivňují váš web
Řešení problémů s hostingem23.04.2026

Sousedé na sdíleném hostingu: jak ovlivňují váš web

Jak sousedé na sdíleném hostingu ovlivňují rychlost a poštu vašeho webu: test se dvěma pooly PHP-FPM, role limitů a cache, kontrola TTFB,…