Staging Guard

Stop staging sites from emailing real customers and showing up in Google. Colours the admin bar so nobody edits the wrong copy.

  • Versionv1.3.0
  • ReleasedOct 20, 2026
  • WordPress6.6+
  • Tested up to7.1
  • PHP7.4+
  • LicenseGPL

What it does

Every agency has a staging story. A copy of the live database goes to staging, and then:

  • a scheduled task emails every customer from the staging site,
  • Google indexes staging.example.com and it outranks the real site,
  • someone spends an hour editing staging, thinking it was live.

Staging Guard prevents all three, on every copy that is not the live site.

On staging, development and local copies it:

  • Blocks all outgoing email, or sends it all to one test address with the real recipients shown in the subject. Cc and Bcc recipients are removed too.
  • Lets your own team keep receiving email: list addresses or whole domains (@youragency.com) under "Always deliver to".
  • Keeps a list of the last 50 stopped emails (recipient, subject and time), so you can see what would have gone out. Turn on "keep message bodies" to check email content as well.
  • Sends a test email from the settings screen, so you can see exactly what happens to email from this copy.
  • Hides the site from search engines with an X-Robots-Tag header, a noindex, nofollow robots meta tag and a robots.txt that blocks everything, and turns off the XML sitemap.
  • Colours the whole admin bar (orange for staging, blue for development, green for local) and labels it, on the dashboard and on the front end.
  • Adds the environment to every browser tab title, for example "[Staging] Posts", so the right tab is easy to find.
  • Adds a Site Health check that warns when email from a staging copy could still reach real people, or when staging copies of the live site would not be detected.

On the live site it does nothing.

Free and open source under the GPL. No pro version, no upsells, no ads in wp-admin, no tracking. And it stays that way.

How it knows which copy is live

  1. If WP_ENVIRONMENT_TYPE is set in wp-config.php or in the server environment, it uses that. This is the WordPress standard, and many hosts set it for you.
  2. Otherwise, use the production lock: on the live site, go to Tools > Staging Guard and click "This is the live site". From then on, any copy of the database running on a different domain is treated as staging, with no wp-config.php change.

The lock stores a hash of the live domain instead of the domain itself. Migration tools that search and replace the domain name in the database leave the hash alone, so the lock survives the copy to staging.

If neither is set, WordPress assumes production and Staging Guard stays off.

Settings in wp-config.php

Fix the email behaviour per environment, so a database copy cannot change it:

php
define( 'STAGING_GUARD_MAIL_MODE', 'redirect' ); // block, redirect or allow
define( 'STAGING_GUARD_REDIRECT_TO', '[email protected]' );
define( 'STAGING_GUARD_ALLOWLIST', '@youragency.com, [email protected]' );
define( 'STAGING_GUARD_PRODUCTION_HOSTS', 'example.com, shop.example.com' ); // the live domains
define( 'STAGING_GUARD_KEEP_BODIES', false );
define( 'STAGING_GUARD_NOINDEX', true );
define( 'STAGING_GUARD_LOG_DAYS', 7 );

WP-CLI

  • wp staging-guard status shows the environment, how it was detected, and what is guarded.
  • wp staging-guard lock-production marks the current domain as the live site.
  • wp staging-guard unlock-production removes the lock.
  • wp staging-guard log lists stopped email.
  • wp staging-guard test-email [email protected] sends a test email and reports what happened to it.

Install

Upload

  1. Download staging-guard.zip (v1.3.0).
  2. In wp-admin, go to Plugins > Add New Plugin > Upload Plugin and choose the zip.
  3. Click Install Now, then Activate.

WP-CLI

Install and activate the latest release in one command:

wp plugin install https://github.com/aryanjasala/staging-guard/releases/download/v1.3.0/staging-guard.zip --activate

Compatibility

Checked by the end-to-end tests on every change. Unit tests run on PHP 7.4 to 8.4.
WordPressPHPStatus
WordPress 7.1PHP 8.3Tested
WordPress 6.9PHP 8.3Tested
WordPress 6.6PHP 7.4Tested

Questions

Does it work with SMTP plugins?

Yes, for plugins that send through WordPress’s own wp_mail() function, which is most of them. A few plugins replace wp_mail() entirely. Staging Guard detects this and shows a warning on its settings screen, because it cannot intercept those emails.

Will blocked emails break password resets on staging?

No. Blocked emails are reported to WordPress as sent, so forms and password resets behave normally. Use redirect mode if you need to receive them.

What if I copy the staging database to production?

The production lock is still stored, and the live domain still matches it, so the live site stays unguarded. The search engine setting under Settings > Reading is never changed by this plugin.

Can I change how the environment is detected?

Use the staging_guard_environment filter. It receives an array with type (production, staging, development or local) and source.

What if the live site moves to a new domain?

Then the production lock no longer matches, and the live site would count as staging. Staging Guard warns about this on every admin screen and in Site Health, so it does not go unnoticed. Lock the new domain from Tools > Staging Guard, or list every live domain in STAGING_GUARD_PRODUCTION_HOSTS so a move never trips it. www. and the bare domain already count as the same.

Does the email log keep personal data?

It keeps who an email was for and its subject, and the message only if you turn that on. Entries are deleted after 7 days, and the log is part of the WordPress export and erase personal data tools. The live site keeps no log.

For developers

Every default, limit and message has a filter, and every change fires an action. The full reference, with constants, abilities and WP-CLI, is in REFERENCE.md.

staging_guard_environmentFilter
Change the detected environment, see below( array $environment )
staging_guard_production_hostsFilter
The live site’s domains. Default from STAGING_GUARD_PRODUCTION_HOSTS( string[] $hosts )
staging_guard_mail_policyFilter
The final email handling: mode, redirect_to, allowlist. For example redirect on staging but block on development. A redirect without a valid address still blocks( array $policy, string $environment )
staging_guard_allow_emailFilter
Let one email out untouched and unlogged, such as monitoring alerts to your own team( bool $allow, array $atts )
staging_guard_allowlistFilter
Addresses and @domains that still get email( string[] $allowlist )
staging_guard_redirect_subjectFilter
Subject of a redirected email. Default [STAGING → original recipients] Subject( string $subject, string[] $stopped, array $atts )
staging_guard_noindexFilter
Whether staging copies are hidden from search engines( bool $noindex )
staging_guard_robots_txtFilter
The robots.txt a staging copy serves( string $output )
staging_guard_coloursFilter
Admin bar colour per environment type. Anything but a hex colour falls back to the default( array $colours )
staging_guard_environment_labelFilter
The environment name in the admin bar and on the screens, such as "UAT"( string $label, string $type )
staging_guard_log_sizeFilter
Stopped emails the log keeps at most. Default 1000( int $size )
staging_guard_log_daysFilter
Days a stopped email stays in the log( int $days )
staging_guard_capabilityFilter
Who may see and change Staging Guard. Default manage_options, or manage_network_options on multisite( string $capability )
staging_guard_mail_stoppedAction
Fires when an email is blocked or redirected. $entry has id, to, subject, action, delivered and body( array $entry, array $atts )
staging_guard_production_lockedAction
The production lock was set on this domain( string $domain )
staging_guard_production_unlockedAction
The production lock was removed

Changelog

  1. v1.3.0
    • FixMake the abilities visible to MCP agents
    • NewAdd abilities so AI agents can tell staging from the live site before doing anything risky
  2. v1.2.0
    • NewAdd filters for email handling, robots.txt, colours and labels, and actions for the production lock
    • FixWarn when the live site counts as staging, and move the email log out of one option
  3. v1.1.0
    • New"Always deliver to" allowlist for addresses and @domains
    • NewSend a test email from the settings screen or WP-CLI
    • NewOptionally keep message bodies in the email log
    • NewSite Health check
    • NewEnvironment shown in browser tab titles
    • NewRedesigned settings screen with status cards
    • Rewritten as PSR-4 classes

Every release, with notes: GitHub releases.