There is a specific, sinking feeling known only to DevOps engineers and WordPress developers using WP Engine. You’ve just finished a flawless sprint. Your CI/CD pipeline reports a successful build. The files have been rsync’d or git-pulled to production.
Yet, when you visit the URL, the old site loads. You hard-refresh. Nothing. You open an Incognito window. Still the old content.
To see your changes, you have to log into the WordPress dashboard, hover over the "WP Engine" menu, and manually click "Purge All Caches." This manual step defeats the purpose of Continuous Deployment. If you cannot automate the cache invalidation, you do not have a truly automated pipeline.
This guide details exactly how to programmatically purge WP Engine’s proprietary Varnish and Object caching layers using WP-CLI and PHP, allowing you to integrate cache clearing directly into GitHub Actions, Bitbucket Pipelines, or GitLab CI.
The Root Cause: Why Varnish Doesn't See Your Deploy
To fix the issue, we must understand the architecture. WP Engine utilizes a high-performance stack that relies heavily on Varnish, an HTTP accelerator designed for content-heavy dynamic websites.
Varnish sits in front of your web server (Nginx/Apache). It serves cached copies of pages from memory (RAM) to reduce backend load.
The Disconnect Between Disk and RAM
When you deploy code via Git or SFTP, you are modifying the file system. You are changing PHP files, CSS assets, and templates on the disk.
However, Varnish is serving content from memory. Varnish does not inherently monitor the file system for changes (doing so would be IO-intensive and slow). It relies on Time-To-Live (TTL) settings or explicit HTTP PURGE requests to know when content is stale.
Until the TTL expires (which can be 10 minutes or 10 hours depending on settings) or a purge is explicitly requested, Varnish will continue serving the old version of the site, regardless of what files exist on the disk.
The Solution: The WpeCommon Class
WP Engine does not expose the Varnish administrative port to the public internet, nor do they support standard HTTP PURGE requests from unauthorized IP addresses.
However, their platform includes a Must-Use (MU) plugin that exposes a singleton class called WpeCommon. This class contains the internal methods required to talk to their caching infrastructure.
We will use two primary methods from this class:
purge_memcached(): Clears the object cache.purge_varnish_cache(): Clears the page cache.
Method 1: The Custom WP-CLI Command (Recommended)
The most robust way to handle this in a CI/CD environment is to create a custom WP-CLI command. This allows you to trigger the purge via SSH without exposing public HTTP endpoints.
Step 1: Register the Command
Create a file in your theme or a custom plugin (e.g., wp-content/mu-plugins/deploy-utilities.php).
<?php
/**
* Plugin Name: Deployment Utilities
* Description: Adds CLI commands for CI/CD pipelines.
*/
if ( defined( 'WP_CLI' ) && WP_CLI ) {
class Deploy_CLI_Command {
/**
* Purges WP Engine Varnish and Object Caches.
*
* ## EXAMPLES
*
* wp deploy purge_wpe
*
* @when after_wp_load
*/
public function purge_wpe( $args, $assoc_args ) {
// 1. Check if we are actually on WP Engine
if ( ! class_exists( 'WpeCommon' ) ) {
WP_CLI::error( 'WpeCommon class not found. Are you on WP Engine?' );
return;
}
// 2. Purge Object Cache (Memcached)
try {
if ( method_exists( 'WpeCommon', 'purge_memcached' ) ) {
\WpeCommon::purge_memcached();
WP_CLI::log( '✅ Object Cache (Memcached) purged.' );
}
} catch ( Exception $e ) {
WP_CLI::warning( 'Could not purge Memcached: ' . $e->getMessage() );
}
// 3. Purge Varnish (Page Cache)
try {
if ( method_exists( 'WpeCommon', 'purge_varnish_cache' ) ) {
\WpeCommon::purge_varnish_cache();
WP_CLI::success( '✅ Varnish Cache purged successfully.' );
} else {
WP_CLI::error( 'Method purge_varnish_cache not found.' );
}
} catch ( Exception $e ) {
WP_CLI::error( 'Failed to purge Varnish: ' . $e->getMessage() );
}
}
}
WP_CLI::add_command( 'deploy', 'Deploy_CLI_Command' );
}
Step 2: Testing Locally (simulation)
If you run this locally, it will fail gracefully because WpeCommon doesn't exist. On WP Engine, running wp deploy purge_wpe via SSH will now trigger the internal purge mechanisms.
Method 2: The Secure Webhook (Alternative)
If you cannot use SSH in your pipeline (due to security policies or limitation of the runner), you can create a secured REST API endpoint.
Security Warning: Do not leave this endpoint open. We will use a Bearer token verification to ensure only your CI pipeline can trigger a cache wipe.
<?php
/**
* Register a REST route to purge caches.
* endpoint: /wp-json/deploy/v1/purge
* method: POST
* header: Authorization: Bearer YOUR_SECRET_KEY
*/
add_action( 'rest_api_init', function () {
register_rest_route( 'deploy/v1', '/purge', [
'methods' => 'POST',
'callback' => 'wpe_programmatic_purge',
'permission_callback' => function ( WP_REST_Request $request ) {
// Get the authorization header
$auth_header = $request->get_header( 'authorization' );
// Define your secret in wp-config.php as WPE_DEPLOY_SECRET
if ( ! defined( 'WPE_DEPLOY_SECRET' ) ) {
return new WP_Error( 'config_error', 'Server misconfigured', [ 'status' => 500 ] );
}
// specific check for "Bearer <token>"
if ( $auth_header !== 'Bearer ' . WPE_DEPLOY_SECRET ) {
return new WP_Error( 'forbidden', 'Invalid auth token', [ 'status' => 403 ] );
}
return true;
},
]);
});
function wpe_programmatic_purge() {
if ( ! class_exists( 'WpeCommon' ) ) {
return new WP_REST_Response( [ 'message' => 'Not on WP Engine environment.' ], 200 );
}
\WpeCommon::purge_memcached();
\WpeCommon::purge_varnish_cache();
return new WP_REST_Response( [ 'message' => 'WP Engine Caches Purged' ], 200 );
}
CI/CD Integration: GitHub Actions
Now that we have the mechanism (Method 1), let's implement the automation. Here is a standard GitHub Action step that connects via SSH and runs the WP-CLI command.
Prerequisites
- SSH Keys: Add your SSH private key to GitHub Secrets (
WPE_SSH_KEY). - Public Key: Ensure the matching public key is added to the WP Engine User Portal.
- SSH Host: Usually
[install-name].ssh.wpengine.net.
The Workflow YAML
name: Deploy to Production
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
# ... (Your rsync or git push steps go here) ...
- name: Purge WP Engine Cache via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.WPE_SSH_HOST }}
username: ${{ secrets.WPE_SSH_USER }}
key: ${{ secrets.WPE_SSH_KEY }}
script: |
# Navigate to the WordPress root
cd /sites/${{ secrets.WPE_INSTALL_NAME }}
# Check if WP-CLI is working
wp core is-installed
# Run our custom command
wp deploy purge_wpe
Deep Dive: Why wp cache flush Isn't Enough
A common misconception is that running the standard wp cache flush command is sufficient.
On a standard WordPress install, wp cache flush clears the Object Cache. On WP Engine, this clears Memcached. While this ensures that database queries are re-run, it does not signal the Varnish layer to expire the HTML page cache.
If you only run wp cache flush, your users will still see the old HTML pages until the Varnish TTL naturally expires. You explicitly need the purge_varnish_cache() hook which communicates with the Varnish daemon via a proprietary socket or internal API restricted to the WpeCommon class.
Common Pitfalls and Edge Cases
1. The "White Screen" on Deploy
If you purge the cache while files are still uploading, a user might request a page, get a broken PHP execution (due to missing files), and Varnish will cache that 500 or 502 error. Fix: Always ensure your wp deploy purge_wpe step happens after the file transfer is fully complete.
2. Permissions Issues
Occasionally, the SSH user may differ from the PHP-FPM user. However, on WP Engine, permissions are generally unified. If you encounter permission errors, ensure your SSH user has "Write" access in the WP Engine portal.
3. Local Development
Remember to wrap all calls to WpeCommon in class_exists() checks. Your local Docker or XAMPP environment does not have this class. Failing to wrap it will cause a Fatal Error on your local machine.
Conclusion
Automating your cache invalidation is the final mile of a professional deployment pipeline. By utilizing WpeCommon and wrapping it in a custom WP-CLI command, you bridge the gap between file system deployments and memory-based caching.
This approach ensures that every time you merge to main, your stakeholders and users see the latest features immediately, eliminating the "I don't see the changes" feedback loop forever.