Back to blog
Guides

How to set up a staging site for WordPress and Laravel

Testing changes on the live site works until the day it does not. A proper staging environment is a copy you can break safely, but only if you handle the database sync, URLs, search engines and outgoing email correctly. Here is how to do staging right, and how to promote changes without surprises.

S
ShadowPanel Team
7 min read

Every developer has a "just a quick change on production" story. A plugin update that white-screened a shop on a Friday afternoon. A Laravel migration that locked a table during peak traffic. A theme tweak that looked fine on your laptop and broke the checkout on mobile. A staging environment exists so those stories happen somewhere nobody is watching. But a staging site done carelessly causes its own disasters: customers receiving test emails, Google indexing a duplicate of your site, or a "promote" that overwrites a day of real orders. Here is how to do staging properly for WordPress and Laravel.

What a staging environment actually is

Staging is a second copy of a production site, running on the same software stack, with its own files, its own database and its own address, usually a subdomain such as staging.example.com. The rules that make it useful:

  • Same stack as production. Same PHP version, same extensions, same web server config. A staging site on PHP 8.3 tells you nothing about how a change behaves on the PHP 8.1 that production runs. (If you host several sites, see running multiple PHP versions per site.)
  • Its own database. Never point staging at the production database. A test that deletes rows deletes real rows.
  • Recent data. Staging that was copied a year ago does not reproduce today's bugs. Refresh it from production regularly.
  • Isolated from the outside world. No search engine indexing, no real emails, no live payment keys.

Copying production to staging

The basic move is the same for any PHP application: copy the files, copy the database, then fix what differs.

# Files (exclude caches and large uploads you do not need)
rsync -a --delete --exclude 'wp-content/cache' \
  /var/www/site/public_html/ /var/www/site/staging/

# Database: dump production, load into the staging database
mysqldump --single-transaction site_prod | mysql site_stg

--single-transaction gives you a consistent snapshot of InnoDB tables without locking the live site while the dump runs.

WordPress: search-replace the URLs

WordPress stores its own URL in the database, often inside serialized PHP arrays (widget settings, page builder data). A plain SQL REPLACE() corrupts serialized strings because the stored length no longer matches. Use wp-cli, which understands serialization:

wp --path=/var/www/site/staging search-replace \
  'https://example.com' 'https://staging.example.com' \
  --all-tables --precise --dry-run
# Looks right? Run it again without --dry-run

Then make sure wp-config.php on staging points at the staging database with staging credentials, and set define('WP_ENVIRONMENT_TYPE', 'staging'); so plugins that check the environment behave accordingly.

Laravel: a separate .env

Laravel keeps environment differences out of the database, which makes staging simpler. Staging gets its own .env:

APP_ENV=staging
APP_DEBUG=false
APP_URL=https://staging.example.com
DB_DATABASE=site_stg
MAIL_MAILER=log
QUEUE_CONNECTION=sync

Run php artisan migrate against staging first. If a migration fails or takes ten minutes on a copy of real data, you just learned something production would have taught you the hard way. Clear and rebuild the config cache (php artisan config:cache) after editing .env, or Laravel keeps using the old values.

Keep staging invisible and harmless

This is where most staging setups fail.

  • Block search engines. A public copy of your site is duplicate content. Protect staging with HTTP basic auth, which keeps out crawlers and curious visitors at the same time. At minimum send a noindex header:
add_header X-Robots-Tag "noindex, nofollow" always;

In WordPress, also tick "Discourage search engines" (wp option update blog_public 0). A robots.txt disallow alone is not enough; it stops crawling, not indexing of links found elsewhere.

  • Turn outgoing email off. A copied shop database contains real customer addresses. One cron run on staging can send "your order has shipped" to hundreds of people. In Laravel use MAIL_MAILER=log; in WordPress use a mail-catching plugin or route mail to a sandbox like Mailpit.
  • Swap payment keys to test mode. Stripe, PayPal and friends all have sandbox keys. Staging must never hold live ones.
  • Disable or isolate scheduled jobs. Cron jobs, queue workers and webhooks that talk to third parties (CRM sync, ERP export) should be off or pointed at test endpoints.
  • Anonymise if you can. For agencies handling client data, scrubbing personal data on staging is often a GDPR expectation, not just good manners.

Promoting: the step that needs the most care

"Push staging to live" sounds like one action. It is really two very different ones:

  • Code and files move from staging to production. This is usually safe and is what you want.
  • The database is where it gets dangerous. Between your last sync and now, production kept taking orders, comments, form submissions and new users. Overwriting the production database with the staging copy throws all of that away.

Practical rules:

  1. Back up production immediately before promoting. Files and database, stored off the server. (See encrypted website backups to S3.)
  2. Promote files freely, the database rarely. For content sites that change slowly, a full promote right after a fresh sync is fine. For shops and membership sites, replay database changes as migrations (Laravel) or redo settings changes by hand instead of copying the whole database.
  3. Refresh staging right before the work, not weeks earlier. The shorter the gap between sync and promote, the less production data is at risk.
  4. Promote in a quiet window and check the live site straight after: home page, checkout or login, the PHP error log.
  5. Reverse the URL replace. If you do copy a WordPress database back, run the search-replace the other way, or the live site will start linking to the staging subdomain.

Common traps

  • Staging pointed at the production database "temporarily".
  • Search engines indexing the staging subdomain because nobody added auth or noindex.
  • Test emails reaching real customers from a copied order table.
  • A different PHP version on staging, so bugs only appear after promotion.
  • Promoting a stale staging database and silently deleting a week of orders.
  • Forgetting to update staging after production changes, so tests run against the past.
  • Hard-coded production paths or hostnames left in config files (caching plugins, object-cache settings, custom Laravel config), so staging quietly writes to production's cache or storage.
  • Sharing one Redis database or one uploads directory between both environments, so a cache flush or a deleted file on staging hits the live site too.

How ShadowPanel handles staging

ShadowPanel is a self-hosted hosting control panel, and on the Pro and Agency plans every site can get a staging environment:

  • One click to create. The panel provisions a staging. subdomain of the site with its own document root and its own dedicated database, on the same PHP version as production.
  • Refresh from production. One action overwrites staging with a fresh copy of the production site, so you test against current data.
  • Promote when ready. Another action pushes the staging copy live, behind a confirmation step, with a log of the last sync shown in the panel.
  • Destroy cleanly. Remove the staging environment and its database when the project is done.
  • Pairs with the rest of the panel. Take an on-demand backup of production before a promote, and on WordPress sites use the WordPress Toolkit to try updates on staging first. (More on that routine in managing many WordPress sites.)

The environment-specific parts stay in your hands: setting staging credentials in wp-config.php or .env, the URL search-replace, mail and payment settings, and protection against indexing. Those depend on your application, and the checklist above covers them.

The bottom line

Staging is cheap insurance, as long as it is a faithful copy that cannot hurt anyone: same stack, its own database, recent data, no indexing, no real emails, no live keys. Treat promotion as the risky step it is: back up first, move files freely, and move databases only when you are sure nothing new happened on production. ShadowPanel gives every Pro and Agency site a staging copy you can create, refresh and promote from the panel.

curl -fsSL https://shadowpanel.de/install.sh | bash

See pricing and download →

We use cookies

We use cookies to improve your experience and remember your preferences. You can manage your choices below. Learn more.

ShadowPanel

Manage your cookie preferences below. Essential cookies are always active as they are required for the website to function.

Essential

Required for the site to work. These include session authentication and CSRF protection. Cannot be disabled.

Always on

Functional

Remembers your language preference so you do not need to reselect it on every visit.

Analytics

Helps us understand how visitors use the site so we can improve it. No personal data is sold.

Marketing

Used by Google Ads to measure ad conversions and improve campaign targeting. No personal data is sold.