Workflow
How to Manage WordPress Snippets Across Multiple Sites: A Complete Guide
Copy-pasting the same PHP into ten dashboards is how versions drift. A complete guide to sharing, configuring, rolling out, and retiring snippets across client, staging, and production sites.
Updated 8 min read Mark Ashton
- wordpress
- workflow
- github
- agencies
The first snippet on a new client site is easy. The twelfth copy of "disable XML-RPC and hide the admin bar for subscribers" is how you end up with four versions, one of which still has last year's analytics ID.
Managing snippets across multiple sites is not a multisite-network feature. It is a distribution problem: one reviewed piece of code, many WordPress installs, each with slightly different plugins, PHP versions, and settings. This guide covers the whole lifecycle — choosing a source of truth, structuring a shared library, handling per-site differences, rolling changes out safely, and retiring code without leaving ghosts behind.
For the people-and-process side (roles, review, quarterly audits), see WordPress code management for agencies. This article is the operational manual.
Why copies drift
Every snippet that only exists inside one wp-admin invites drift:
- A hotfix on production never makes it back to staging
- Site B was "almost the same," so someone edited in place
- A cloud library pushed a bundle, then a contractor edited one copy locally
- The snippet was pasted from a Slack thread that was already two revisions behind
None of these are mistakes by bad developers. They are what happens when copying is the easiest path. The fix is to make the correct path — one source, many subscribers — easier than copying.
Pick a source of truth
There are three candidates:
- Git (GitHub or GitLab) is the right default as soon as more than one person or more than one production site is involved. Snippet files are the repository. Sites pull. Hotfixes go back as commits. The full setup is in how to version control WordPress snippets with GitHub.
- A vendor cloud library (Code Snippets Pro's cloud, WPCode's library, WPCodeBox) is acceptable for a solo freelancer with a small starter pack who will never need a pull request. You are renting the source of truth, and there is no review step between "save" and "every site has it."
- A production site is never the source of truth. It is a deploy target that happens to have an editor.
If a client security questionnaire asks where custom PHP lives, "our GitHub organization, with review history" and "a third-party snippet cloud" are very different answers.
Structure the shared library
Split code by how widely it is shared:
wp-snippets/
├── _common/ # every site: hardening, admin cleanup, headers
├── _woocommerce/ # every store
└── clients/
├── acme/ # Acme only
└── globex/ # Globex onlyRules that keep this sane:
- Name snippets after the job:
woo-checkout-hide-company-field, notacme-fix-2. - One job per snippet. If you need to disable half a snippet on one site, it should have been two snippets.
- Shared code is reviewed by an owner. A change to
_commonaffects every client; route it through a lead with CODEOWNERS.
In SnipVault, project workspaces give each client or concern its own folder tree inside the admin, so "Acme checkout" and "Acme editorial" are not one flat list of 200 items.
Handle per-site differences without forking
The most common reason copies drift is a small per-site difference: a different ID, a different threshold, a feature that should only run on some sites. Forking the snippet for each site recreates the drift problem. Keep one snippet and move the difference into configuration.
Configuration in wp-config.php
Put per-site values in constants that live with the site, not the snippet:
// wp-config.php on acme.com
define( 'AGENCY_GA4_ID', 'G-ACME1234' );
define( 'AGENCY_FREE_SHIPPING_MIN', 75 );<?php
// _common/ga4-tag.php — identical on every site
defined( 'ABSPATH' ) || exit;
add_action( 'wp_head', function () {
if ( ! defined( 'AGENCY_GA4_ID' ) || is_user_logged_in() ) {
return;
}
$id = esc_js( AGENCY_GA4_ID );
printf(
'<script async src="https://www.googletagmanager.com/gtag/js?id=%1$s"></script>' .
'<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag("js",new Date());gtag("config","%1$s");</script>',
$id
);
} );A site without the constant simply skips the snippet. There is nothing to fork, and secrets never enter the repository. SnipVault's PHP Global Variables settings screen does the same job from the admin when you cannot edit wp-config.php.
Environment-aware snippets
WordPress has a built-in environment flag. Set WP_ENVIRONMENT_TYPE to production, staging, development, or local in each site's wp-config.php, then branch on it:
<?php
// _common/staging-guardrails.php
defined( 'ABSPATH' ) || exit;
if ( 'production' !== wp_get_environment_type() ) {
// Never email real customers from staging.
add_filter( 'pre_wp_mail', '__return_false' );
// Keep staging out of search results.
add_filter( 'wp_robots', 'wp_robots_no_robots' );
}This one snippet runs everywhere and behaves correctly everywhere — which is the whole point of sharing it.
Scope with conditions, not copies
When a snippet should only run on certain pages, roles, or post types, use the plugin's conditional loading instead of an if buried in the code. Conditions are visible in the admin, reviewable, and easy to change per site without editing PHP.
Check version parity before you share
"Works on my site" breaks down when sites differ. Before promoting a shared snippet, know the spread across your fleet:
- PHP version. The oldest production host sets the syntax you are allowed to use. Lint against it in CI.
- WordPress version. New core functions are not available everywhere. Guard with
function_exists. - Plugin versions. WooCommerce, Gravity Forms, and ACF hooks change between majors. Guard with
class_existsordefined, and note requirements in the snippet header.
if ( ! class_exists( 'WooCommerce' ) || version_compare( WC()->version, '9.0', '<' ) ) {
return;
}A guard turns "fatal error on the one site still on an old version" into "snippet quietly does nothing there" — and your inventory tells you which site needs an upgrade.
Roll out in stages
A change to a shared snippet is a deploy to every site at once unless you stage it. A rollout that does not need a spreadsheet:
- Review once. A PR with lint passing and a security audit for anything touching users, payments, or SQL. The safety article sets the bar.
- Staging first. Pull on the staging site that most resembles production — same WooCommerce version, same PHP. Playground is good for parse and hook tests (Playground workflow); staging is for carts and logins.
- Canary. Promote to one low-traffic production site and watch it for a day: error logs, checkout, forms.
- Fleet. Pull on the remaining sites.
- Confirm. Load the front end, checkout, and
wp-adminon each. If you cannot name the pages the snippet touches, it should not have been global.
Promote by pull, not by paste. Each site pulls the reviewed file. Nobody opens a gist in ten tabs.
Moving between sites quickly
The slowest part of a fleet rollout is logging in to every site. SnipVault's remote sites feature lets you register other installs in settings and switch the active site from a selector, so you can check and sync each site from one admin instead of juggling ten sets of credentials. Use it alongside Git: Git holds the code, remote sites make operating the fleet fast.
What to do with genuine one-offs
Some code really is per-site: a pixel, a custom post type slug the client invented, an integration only one client pays for. Keep it in that client's folder or workspace, clearly named, never in the shared tree.
The failure mode is putting a one-off in the shared pack "just this once." Six months later every new site inherits a Klaviyo key from a different client.
Retire snippets across the fleet
Deleting is harder than deploying. If you remove legacy-ga-ua from Git and one site never pulls, that site keeps shipping it indefinitely.
- Deactivate first. Ship the snippet inactive, or replace its body with a no-op that logs, for one release cycle.
- Pull everywhere you can reach, and note which sites you could not.
- Delete from the repository once every site has pulled.
- Keep the history. "Why did we ever have this?" is a question someone will ask at 2am.
File-based storage helps here: during an audit you can see every snippet file on disk. Database-only plugins hide retired code as table rows until someone exports them.
Multisite is a different problem
WordPress multisite runs many sites from one installation, so network-activated code reaches all of them at once. That is one runtime with many sites. It does not help when your sites are twenty separate installs at twelve hosts — which is how most agencies actually work. A "multisite compatible" snippet plugin will not manage unrelated client servers; that needs a shared source of truth and a way to operate each install.
Tools, ranked by how badly they drift
- Copy-paste between dashboards — maximum drift, still the industry default
- Export/import files — a snapshot, stale the moment someone edits a site
- Vendor cloud push — low friction, weak review, easy to spread a mistake fleet-wide
- Git + manual pull on each site — honest and auditable, but slow to operate
- Git + GitHub sync + remote sites — the honest path, without the slowness
A quick audit for your current setup
Answer these for every production site:
- Where does custom code live? (It should be one place.)
- Who last changed the checkout snippets, and where is the diff?
- Which snippets are shared and which are site-specific?
- Which PHP and WooCommerce versions is the oldest site running?
- How would you disable one snippet if the front end were down?
- If you retired a snippet last quarter, which sites still run it?
The test of a good setup
Ask a colleague to add a one-line fix to the XML-RPC snippet and get it onto staging plus two production sites without pasting PHP into Slack. If the answer involves a zip file or "I'll just hop on each site," you do not have multi-site management. You have a list of logins.
Make the repository the product. Make each WordPress install a subscriber. Everything else is nostalgia for when you had three sites and a good memory.