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

Staging WordPressu: jak ho vytvořit, skrýt a přenést změny

Staging WordPressu: jak ho vytvořit, skrýt a přenést změny

Staging WordPressu je úplná kopie vašeho webu na samostatné subdoméně, například staging.example.com, s vlastní databází, kde aktualizujete pluginy a šablony bez rizika pro ostrý web. Vytvoříte ho v pěti krocích: zkopírujete soubory, zkopírujete databázi, nahradíte adresy URL příkazem wp search-replace (ne sedem), kopii zamknete heslem a noindex a vypnete v ní e-maily a cron. Změny se na ostrý web vracejí jako soubory, ne jako databáze: na našem testovacím stroji přepsání ostré databáze databází ze stagingu smazalo 7 nových objednávek a 5 komentářů a ostrý web přepnulo na noindex.

Jak jsme testovali

10. listopadu 2026 na testovacím stroji: kontejner Ubuntu 24.04, nginx 1.24 před Apache 2.4, PHP-FPM 8.3, MariaDB 10.11, WordPress 7.1.4, WooCommerce 11.2.2 a WP-CLI 2.12. „Ostrý“ web měl 25 produktů z ukázkových dat WooCommerce, 300 objednávek, 41 komentářů a tři fotky po 5 MB. Kopii pro staging jsme vytvořili na subdoméně stejného serveru a lokální lapač pošty zachytával každou zprávu, místo aby ji odeslal. Časy a velikosti jsme měřili na lokálním disku testovacího stroje; nejde o hodnoty serverů RichHost. Dokumentaci WP-CLI a Google jsme ověřili týž den.

Staging v šesti krocích: příkaz a kontrola

Příkazy wp spouštějte přes SSH ve složce webu. Cesty platí pro typické uspořádání, kde web leží v ~/web/domain/public_html.

Krok Příkaz Jak ověřit
1. Subdoména a prázdná databáze Panel: přidejte subdoménu jako webovou doménu, pak vytvořte databázi Subdoména se otevře, nová databáze nemá žádné tabulky
2. Kopie souborů rsync -a ze složky ostrého webu do složky subdomény find . -type f | wc -l vrátí v obou složkách stejné číslo
3. Kopie databáze s novými adresami wp search-replace s --export, údaje nové databáze do wp-config.php kopie, wp db import wp option get siteurl na kopii ukáže subdoménu
4. Skrýt před vyhledávači a cizími lidmi wp option update blog_public 0, heslo a X-Robots-Tag v .htaccess curl -I bez hesla vrátí 401 a X-Robots-Tag
5. Vypnout cron a e-maily DISABLE_WP_CRON, WP_ENVIRONMENT_TYPE, mu-plugin blokující poštu V logu není POST /wp-cron.php, testovací objednávka nepošle e-mail
6. Přenést změny zpět rsync --dry-run --itemize-changes, jen šablony a pluginy Počet objednávek a komentářů na ostrém webu se nezmění

Subdoména a kopie souborů

V ovládacím panelu se subdoména obvykle přidává jako každá jiná webová doména, s názvem staging.example.com. Panel vytvoří samostatnou složku s public_html, a pokud je na serveru i zóna DNS domény, obvykle přidá záznam pro subdoménu. SSL zapnete v nastavení domény stejně jako u hlavního webu. Databázi pro kopii vytvořte v sekci databází; mnoho panelů před názvy databáze a uživatele přidává název účtu, proto si poznamenejte celé názvy.

Soubory můžete zkopírovat ve správci souborů panelu, ale u tisíců malých souborů je jednodušší se připojit přes SSH a použít rsync. Než začnete, udělejte čerstvou zálohu ostrého webu: staging zálohu nenahrazuje.

rsync -a ~/web/example.com/public_html/ ~/web/staging.example.com/public_html/
find ~/web/example.com/public_html -type f | wc -l
find ~/web/staging.example.com/public_html -type f | wc -l

Kolik to zabere v praxi. Testovací obchod měl 224 MB a 10 547 souborů: jádro WordPressu se všemi přibalenými šablonami 136 MB, pluginy 55 MB (skoro 5500 souborů, hlavně WooCommerce), média 33 MB. Databáze měla v MariaDB 5,1 MB, 48 tabulek.

Operace na testovacím stroji Čas Výsledek
rsync souborů na stejném disku 1,1 s 224 MB, 10 547 souborů
Archiv tar.gz všech souborů 15,3 s 104 MB
Dump databáze (wp db export) 0,33 s 1,2 MB SQL, 146 KB po gzip
wp search-replace s exportem do souboru 1,1 s 6 náhrad
Import databáze do kopie 1,9 s 48 tabulek

Na jednom serveru trvá kopie malého obchodu sekundy; nejpomalejší je archivace. Pokud se web stěhuje na jiný server, postup je stejný jako při přesunu WordPressu na jiný hosting.

Náhrada adres v databázi: proč sed rozbije nastavení

WordPress ukládá některá nastavení (widgety, volby šablony a pluginů) jako serializovaná pole PHP, kde před každým řetězcem stojí jeho délka, například s:152:"...". Když adresu v dumpu nahradíte obyčejným sedem, text se prodlouží, ale číslo zůstane stejné. Vyzkoušeli jsme to na textovém widgetu se dvěma odkazy na ostrý web:

$ sed 's#http://live.cw14.test:14101#http://staging.live.cw14.test:14101#g' live.sql > staging_sed.sql
$ wp db import staging_sed.sql
...s:4:"text";s:152:"<a href="http://staging.live.cw14.test:14101/shop/">Go to the shop</a> | <img src=...
PHP Warning:  unserialize(): Error at offset 235 of 272 bytes
bool(false)

# after one page view:
a:1:{s:12:"_multiwidget";i:1;}

Řetězec měl teď 168 znaků (o 8 víc na každou adresu) a PHP ho nedokázalo přečíst. Web dál vracel 200 bez jakékoli chyby; widget prostě zmizel. Ještě horší je, že hned při prvním zobrazení stránky WordPress přepsal poškozenou hodnotu prázdnou, takže obsah widgetu byl z databáze stagingu nadobro pryč. Proto si takového poškození často nikdo nevšimne: nastavení se potichu vrátí na výchozí hodnoty. Past je zrádná i tím, že sed funguje, když má nová adresa stejnou délku, a zvyk zůstane.

WP-CLI serializovaná data rozbalí, změní text a zabalí je zpět se správnou délkou. Nejdřív ho spusťte s --dry-run: ukáže, co a kde se změní, aniž by cokoli zapsal.

$ wp search-replace 'http://live.cw14.test:14101' 'http://staging.live.cw14.test:14101' --skip-columns=guid --dry-run --report-changed-only
Table       Column        Replacements  Type
wp_options  option_value  3             PHP
wp_posts    post_content  2             SQL
wp_users    user_url      1             SQL
Success: 6 replacements to be made.

Tři ze šesti náhrad byly v serializovaných volbách (typ PHP), takže sed by rozbil polovinu změn. Sloupec guid jsme vynechali záměrně: je to identifikátor příspěvku, ne odkaz, a 337 řádků v něm si ponechalo starou adresu. Pro staging je nejpohodlnější spustit náhradu na ostrém webu s --export. Ostrá databáze zůstane nedotčená a výsledek půjde rovnou do souboru, který naimportujete do kopie.

cd ~/web/example.com/public_html
wp search-replace 'https://example.com' 'https://staging.example.com' --skip-columns=guid --export=$HOME/staging.sql

cd ~/web/staging.example.com/public_html
wp config set DB_NAME user_staging
wp config set DB_USER user_staging
wp config set DB_PASSWORD 'new-db-password'
wp db import $HOME/staging.sql
wp option get siteurl
rm $HOME/staging.sql

Pokud WP-CLI nemáte, použijte migrační plugin, který výslovně podporuje serializovaná data. Najít a nahradit v textovém editoru má stejnou vadu jako sed.

Jak udržet staging mimo Google

Každý nejdřív zaškrtne v nastavení čtení políčko, které žádá vyhledávače, aby web neindexovaly (volba blog_public nastavená na 0). Ověřili jsme, co přesně ve WordPressu 7.1.4 mění:

  • stránky HTML dostanou <meta name='robots' content='noindex, nofollow' />;
  • z robots.txt zmizí řádek Sitemap a wp-sitemap.xml vrací 404;
  • v robots.txt není pravidlo Disallow: / ani hlavička X-Robots-Tag;
  • obrázky z uploads se servírují s kódem 200 a bez jakéhokoli označení: meta značka v HTML se na ně nevztahuje.

Políčko tedy jen žádá, aby se stránky neindexovaly, zatímco kopie se všemi daty je otevřená každému, kdo zná adresu. Zablokovat všechno v robots.txt také není dobrý nápad: Google uvádí, že u adresy blokované v robots.txt robot pravidlo noindex nikdy neuvidí a adresa se přesto může objevit ve výsledcích díky odkazům (dokumentace Google, ověřeno 10.11.2026). Bezpečnější je dát subdoménu za heslo a přidat hlavičku X-Robots-Tag, která pokryje i obrázky a PDF.

Pokud web běží na Apache (jako na našem testovacím stroji s nginx před Apache), přidejte pravidla na začátek souboru .htaccess kopie. Soubor s hesly mějte mimo public_html. Na serveru jen s nginx se totéž udělá direktivami auth_basic v konfiguraci webu.

htpasswd -cB ~/web/staging.example.com/.htpasswd tester

# top of ~/web/staging.example.com/public_html/.htaccess
Header always set X-Robots-Tag "noindex, nofollow"
AuthType Basic
AuthName "Staging"
AuthUserFile /home/user/web/staging.example.com/.htpasswd
Require valid-user

Ověřte výsledek tak, jak web vidí robot vyhledávače, tedy bez hesla a s jeho User-Agentem:

$ curl -I -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" http://staging.live.cw14.test:14101/
HTTP/1.1 401 Unauthorized
X-Robots-Tag: noindex, nofollow
WWW-Authenticate: Basic realm="Staging"

Bez hesla vrátila hlavní stránka i obrázek 401, špatné heslo také 401 (log Apache: AH01617: user tester: authentication failure). Se správným heslem vracely stránky i obrázky 200 s hlavičkou X-Robots-Tag. Ostrého webu se to nedotklo: běžná meta značka robots a funkční sitemapa.

E-maily a cron: kopie nesmí žít vlastním životem

Kopie databáze obsahuje adresu každého zákazníka, takže staging jim může posílat e-maily. Testovací objednávku #35 jsme na kopii označili jako dokončenou, jak by to udělal každý, kdo testuje obchod, a lapač okamžitě přijal Your CW14 Shop order is now complete adresovaný zákazníkovi. Na skutečném hostingu by ten e-mail dorazil skutečnému člověku.

Nejjednodušší ochranou je označit kopii jako stagingové prostředí a zablokovat její poštu filtrem pre_wp_mail v mu-pluginu. Na ostrém webu s typem prostředí production nic neblokuje.

cd ~/web/staging.example.com/public_html
wp config set WP_ENVIRONMENT_TYPE staging
wp config set DISABLE_WP_CRON true --raw
mkdir -p wp-content/mu-plugins

# wp-content/mu-plugins/staging-no-mail.php
<?php
/* Plugin Name: Staging: no outgoing mail */
add_filter('pre_wp_mail', function ($return, $atts) {
    if (wp_get_environment_type() === 'production') {
        return $return;
    }
    $to = is_array($atts['to']) ? implode(',', $atts['to']) : $atts['to'];
    error_log('STAGING mail blocked: ' . $to . ' | ' . $atts['subject']);
    return false;
}, 10, 2);

Potom další testovací objednávka ani formulář pro obnovu hesla neposlaly jediný e-mail a v logu PHP se objevily řádky typu STAGING mail blocked: customer2.seed@example.com | Your CW14 Shop order is now complete. Pluginy, které posílají poštu přes vlastní API a obcházejí wp_mail, tento filtr nezastaví: na kopii je vypněte nebo je přepněte na testovací klíče.

S cronem je to podobné. WP-Cron spouštějí návštěvy a s databází se do kopie přeneslo 12 naplánovaných událostí, včetně fronty úloh WooCommerce na pozadí. Před nastavením hesla každé zobrazení stránky na stagingu vyvolalo POST /wp-cron.php s odpovědí 200. Po přidání hesla jsme si všimli vedlejšího efektu: interní požadavek WordPressu sám na sebe dostal 401 a událost naplánovaná na okamžité spuštění zůstala ležet ve frontě. Cron na kopii se potichu zastavil, takže každý test naplánovaných úloh by dával falešné výsledky. Konstanta DISABLE_WP_CRON to dělá jednoznačným: po jejím nastavení v logu nebyly žádné požadavky na wp-cron.php a úlohu můžete spustit ručně příkazem wp cron event run --due-now. Pokud na ostrém webu volá WP-Cron cron úloha na serveru, pro subdoménu ji nekopírujte.

Jak přenést změny zpět a nepřijít o objednávky

Staging je snímek z okamžiku kopírování, zatímco ostrý web běží dál. Nasimulovali jsme typickou situaci. Na kopii jsme nainstalovali malý plugin, změnili jeden řádek CSS v šabloně, vytvořili stránku a během testování označili dvě objednávky jako dokončené. Mezitím na ostrý web přišlo 7 nových objednávek a 5 komentářů. Pak jsme vyzkoušeli dva způsoby přenosu zpět.

Ukazatel Ostrý web před přenosem Staging Ostrý web po přepsání databáze Ostrý web po přenosu souborů
Objednávky 307 300 300 307
Komentáře 46 41 41 46
Testovací stavy „dokončeno“ 0 2 2 0
blog_public 1 0 0 (noindex na ostrém webu) 1
Nový plugin není aktivní v databázi, soubory chybí, WordPress ho deaktivoval aktivní

Přepsání databáze napáchalo tři škody najednou: smazalo vše, co na ostrý web přibylo po vytvoření kopie, přeneslo změny testovacích objednávek a přepnulo ostrý web na noindex, protože blog_public je uložené v databázi. Ostrý web jsme obnovili z dumpu pořízeného před přenosem a operaci zopakovali správně, jen se soubory:

$ cat push-exclude.txt
wp-config.php
.htaccess
wp-content/uploads/
wp-content/cache/
wp-content/mu-plugins/staging-no-mail.php
wp-content/upgrade/

$ rsync -a --delete --dry-run --itemize-changes --exclude-from=push-exclude.txt staging/ live/
cd+++++++++ wp-content/plugins/cw14-delivery-note/
>f+++++++++ wp-content/plugins/cw14-delivery-note/cw14-delivery-note.php
>f.st...... wp-content/themes/twentytwentyone/style.css

Zkušební běh vypsal přesně ty tři změny, které jsme udělali, a nic jiného. Po ostrém běhu zbývalo jen aktivovat plugin na ostrém webu a znovu tam vytvořit stránku. Objednávky, komentáře i viditelnost ve vyhledávání zůstaly nedotčené. Přepínač --delete maže na ostrém webu soubory, které v kopii chybí, proto ho nikdy nepoužívejte bez zkušebního běhu.

Změny v databázi (stránky, nastavení pluginů, menu) se přenášejí ručně: při testování si zapisujte, co měníte, a zopakujte to na ostrém webu. Trvá to déle než jeden import, ale nic se neztratí.

Co zvolit: když X, udělejte Y

Když Udělejte
Aktualizujete pluginy, šablonu nebo verzi PHP Otestujte na stagingu, pak stejnou aktualizaci zopakujte na ostrém webu, nebo soubory přeneste přes rsync po zkušebním běhu
Změnili jste nastavení, stránky nebo menu Zopakujte změny ručně na ostrém webu podle poznámek
Obchod, fórum, web s registrací uživatelů Nikdy nepřepisujte ostrou databázi databází ze stagingu
Prezentační web bez formulářů a komentářů, hodně změn Výměna databáze je možná: záloha, režim údržby, wp search-replace zpět a blog_public = 1
Bez SSH nebo WP-CLI Migrační plugin s podporou serializovaných dat; sed a textové editory nestačí
Testování je hotové Smažte subdoménu i databázi kopie: zapomenutá kopie se zákaznickými daty je riziko

Staging nepotřebuje samostatný server: stačí subdoména a druhá databáze na stejném účtu, pokud váš tarif povoluje více webů. V hostingových tarifech RichHost je počet webů neomezený, s vlastním ovládacím panelem s asistentem AI a bezplatným SSL Let’s Encrypt.

Časté dotazy

Stačí ve WordPressu políčko „Požádat vyhledávače, aby neindexovaly tento web“?

Ne. Na našem testovacím stroji jen přidalo meta značku noindex ke stránkám HTML. Obrázky zůstaly neoznačené a samotná kopie byla otevřená komukoli. Heslo na subdoméně a k tomu hlavička X-Robots-Tag pokryjí všechno.

Jak často obnovovat staging?

Před každým větším testem si udělejte čerstvou kopii ostrého webu. Stará kopie stagingu zaostává ve verzích pluginů i v datech, takže test na ní může projít a na ostrém webu selhat.

Proč web po přenosu zmizel z Google?

Nejčastější příčinou je, že se blog_public = 0 dostalo na ostrý web spolu s databází. Ověřte to příkazem wp option get blog_public (hodnota má být 1) a ve zdrojovém kódu hlavní stránky hledejte noindex.

Můžu na stagingu testovat platby?

Jen v testovacím režimu platební brány s testovacími klíči. Ostré klíče v kopii databáze vyměňte hned po vytvoření stagingu, spolu s klíči služeb pro rozesílání e-mailů.

Více k tématu

Všechny články
Omezení rychlosti přenosu na hostingu: jak ho poznat a co dělat
Návody a tutoriály k hostingu07.08.2026

Omezení rychlosti přenosu na hostingu: jak ho poznat a co dělat

Jak poznat, že hosting omezuje přenosovou rychlost, jak to sami změřit pomocí curl a co skutečně snižuje objem dat odesílaných ze serveru.

SSH klíče: jak je vytvořit, jaký typ zvolit a jak vypnout hesla
Návody a tutoriály k hostingu27.07.2026

SSH klíče: jak je vytvořit, jaký typ zvolit a jak vypnout hesla

Jak vytvořit SSH klíč, proč je Ed25519 pro nové klíče lepší než RSA, jak ho přidat na server, do panelu hostingu nebo…

IPv6 pro web: co přináší, co se rozbije a kontrolní seznam
Domény a DNS04.07.2026

IPv6 pro web: co přináší, co se rozbije a kontrolní seznam

Co IPv6 přináší webu a co se rozbije, když záznam AAAA existuje, ale server připravený není: listen, ip6tables, certifikát, Let's Encrypt, PHP,…