Transform your WordPress site into a fully self-contained static website that works completely offline.
Static Cache Wrangler automatically creates static HTML versions of your WordPress pages as visitors browse your site. It downloads and localizes all assets (CSS, JS, images, fonts) so the exported site works without an internet connection.
Perfect for:
- Creating offline documentation
- Archiving WordPress sites
- Generating portable demos
- CDN-free deployments
- Client deliverables
- Zero-configuration - Works out of the box
- Automatic generation - Creates static files on page visits
- Asset localization - Downloads CSS, JS, images, fonts to local directory
- Clean HTML output - Removes WordPress-specific meta tags (v2.0.5+)
- Relative paths - All links rewritten for portability
- Modern UI - Clean, card-based admin interface
- WP-CLI support - Full command-line control
- One-click export - Download entire static site as ZIP
- Developer hooks - Extensible API for companion plugins (v2.0.5+)
- Performance profiling - Optional developer tools for benchmarking (v2.0.6+)
- Multisite support - Isolated storage per site with unique namespaces
WordPress.org Compliance Release:
- All template variables now use
stcw_prefix for full compliance - Passes 100% WordPress.org plugin validation
- Enhanced code clarity and documentation
Developer Enhancements:
- New profiling hooks for performance monitoring
- Foundation for optional Performance Profiler MU plugin
- Added
/tools/directory with developer documentation
New Developer Hooks:
stcw_before_file_save- Fires before static file writestcw_after_file_save- Fires after file save completionstcw_before_asset_download- Pre-process asset URLsstcw_after_asset_download- Post-process downloaded assetsstcw_before_asset_batch- Start of async batch processingstcw_after_asset_batch- End of async batch processing
Optional Performance Profiler:
- WP-CLI commands for performance analysis
- Zero overhead when disabled
- Detailed metrics for page generation and asset processing
- See
/tools/performance-profiler.txtfor installation guide - Download latest version: moderncli.dev/code/static-cache-wrangler/performance-profiler/
Enhanced Static HTML Output:
- Removes 7+ WordPress meta tags (RSD, generator, shortlinks, REST API, etc.)
- 3.1% smaller file sizes
- 2.3% faster generation
- Better security (WordPress version hidden)
Developer API:
- New
stcw_remove_wp_head_tagsaction hook - New
stcw_process_static_htmlfilter hook - Extensibility for companion plugins
┌─────────────────────────────────────────────────────────┐
│ Generation Status │ Assets │ Total Size │
│ ENABLED │ 152 │ 4.2 MB │
│ 23 static files │ 3 pending │
└─────────────────────────────────────────────────────────┘
File System Locations
├── Static Files: /wp-content/cache/stcw_static/
├── Assets: /wp-content/cache/stcw_static/assets/
├── Writable: ✓ Yes
└── Size: 4.2 MB
- Download the plugin ZIP
- Go to Plugins > Add New > Upload Plugin
- Upload and activate
wp plugin install static-cache-wrangler --activatecd wp-content/plugins
git clone https://github.com/derickschaefer/static-cache-wrangler.git
wp plugin activate static-cache-wrangler- Navigate to Settings > Static Cache in WordPress admin
- Click Enable Generation
- Browse your site normally - static files are created automatically
- Click Download ZIP when ready
# Enable generation
wp scw enable
# Check status
wp scw status
# Process pending assets
wp scw process
# Create ZIP archive
wp scw zip
# Clear all static files
wp scw clear┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Visitor │─────>│ WordPress │─────>│ Static File │
│ Views Page │ │ Processes │ │ Saved │
└─────────────┘ └──────────────┘ └─────────────┘
│
▼
┌──────────────┐
│ Queue Assets │
│ CSS/JS/Images│
└──────────────┘
│
▼
┌──────────────┐
│ Download │
│ & Localize │
└──────────────┘
- Visitor loads page → WordPress generates HTML
- Plugin captures output → Rewrites all asset URLs to relative paths
- Assets queued → CSS, JS, images, fonts added to download queue
- Background processing → Assets downloaded and localized
- Static file saved → Complete, portable HTML file created
static-cache-wrangler/
├── admin/
│ ├── class-stcw-admin.php # Settings page controller
│ ├── class-stcw-admin-bar.php # WordPress admin bar integration
│ ├── css/
│ │ └── admin-style.css # Admin dashboard styles
│ ├── js/
│ │ ├── admin-script.js # Admin dashboard JavaScript
│ │ └── stcw-admin-bar-handler.js # Admin bar handler
│ └── views/
│ └── admin-page.php # Main settings UI
├── cli/
│ └── class-stcw-cli.php # WP-CLI command definitions
├── includes/
│ ├── class-stcw-core.php # Core functionality & hooks
│ ├── class-stcw-generator.php # HTML generation & output buffering
│ ├── class-stcw-asset-handler.php # Asset downloading & processing
│ ├── class-stcw-url-helper.php # URL manipulation utilities
│ ├── stcw-logger.php # Debug logging utility
│ └── js/
│ └── auto-process.js # Background asset processing
├── tools/
│ └── performance-profiler.txt # Developer profiling guide
└── static-site.php # Main plugin file
Enable static site generation.
Disable static site generation.
Display current status and statistics.
$ wp scw status
Static Generation: Enabled
Static Files: 23
Total Size: 4.2 MB
Pending Assets: 3
Downloaded Assets: 152
Static Directory: /var/www/html/wp-content/cache/stcw_static/
Assets Directory: /var/www/html/wp-content/cache/stcw_static/assets/Process all pending asset downloads immediately.
$ wp scw process
Processing pending assets...
Found 45 pending assets. Processing...
Downloading assets 100% [========================================] 0:00 / 0:12
Downloaded 45 assets successfully!Remove all generated static files and assets.
$ wp scw clear
All static files cleared.Create a ZIP archive of the complete static site.
Options:
--output=<path>- Specify custom output path
# Default location
$ wp scw zip
ZIP created: /wp-content/cache/static-site-2025-01-15-14-30-00.zip (4.2 MB)
# Custom location
$ wp scw zip --output=/tmp/mysite.zip
ZIP created: /tmp/mysite.zip (4.2 MB)The plugin works with sensible defaults, but you can customize behavior:
// Change static files location
define('STCW_STATIC_DIR', WP_CONTENT_DIR . '/my-static-files/');
// Change assets location
define('STCW_ASSETS_DIR', WP_CONTENT_DIR . '/my-assets/');
// Disable async asset processing (process immediately)
define('STCW_ASYNC_ASSETS', false);// Remove additional WordPress head tags
add_action('stcw_remove_wp_head_tags', function() {
remove_action('wp_head', 'your_custom_action');
});
// Modify HTML before saving to file
add_filter('stcw_process_static_html', function($html) {
// Add custom footer, remove tracking scripts, etc.
return $html;
});
// Exclude specific URLs from generation
add_filter('stcw_should_generate', function($should_generate, $url) {
if (strpos($url, '/private/') !== false) {
return false;
}
return $should_generate;
}, 10, 2);// Enable profiling (requires Performance Profiler MU plugin)
define('STCW_PROFILING_ENABLED', true);
// Hook into file save events
add_action('stcw_before_file_save', function($static_file) {
// Start timer or log file path
error_log('Saving: ' . $static_file);
});
add_action('stcw_after_file_save', function($success, $static_file) {
// Log completion
error_log('Saved: ' . $static_file . ' - Success: ' . ($success ? 'Yes' : 'No'));
}, 10, 2);
// Monitor asset downloads
add_filter('stcw_before_asset_download', function($url) {
error_log('Downloading: ' . $url);
return $url;
});
add_filter('stcw_after_asset_download', function($dest, $url) {
error_log('Downloaded: ' . $url . ' to ' . $dest);
return $dest;
}, 10, 2);
// Track async batch performance
add_action('stcw_before_asset_batch', function() {
error_log('Starting asset batch...');
});
add_action('stcw_after_asset_batch', function($processed, $failed) {
error_log("Batch complete: $processed processed, $failed failed");
}, 10, 2);See the Developer Examples for more hook usage patterns.
Version 2.0.6 introduces optional performance profiling capabilities through developer hooks. These hooks enable the Static Cache Wrangler Performance Profiler MU plugin, which provides detailed benchmarking and analysis via WP-CLI.
Installation:
- Download the profiler from moderncli.dev/code/static-cache-wrangler/performance-profiler/
- Place in
/wp-content/mu-plugins/stcw-performance-profiler.php - Enable in
wp-config.php:define('STCW_PROFILING_ENABLED', true);
WP-CLI Commands:
# View performance statistics
wp stcw profiler stats
# View recent profiling logs
wp stcw profiler logs
# Export profiling data to CSV
wp stcw profiler export --output=/tmp/stcw-data.csv
# Clear profiling data
wp stcw profiler clearWhat Gets Profiled:
- Page generation time and memory usage
- File I/O operations
- Asset download performance
- Async batch processing metrics
See /tools/performance-profiler.txt for complete documentation.
The plugin automatically downloads and localizes:
- ✅ CSS files - Stylesheets with nested
url()references processed - ✅ JavaScript files - Scripts with hardcoded asset URLs rewritten
- ✅ Images - All formats (JPG, PNG, GIF, SVG, WebP, AVIF)
- ✅ Fonts - Web fonts (WOFF, WOFF2, TTF, EOT)
- ✅ Standard favicons - Referenced in
<link>tags - ✅ Responsive images - srcset attributes processed
- ✅ Background images - From inline styles
The plugin automatically downloads and localizes standard favicons referenced in <link> tags.
Supported:
- Standard favicon links:
<link rel="icon" href="/favicon.ico"> - PNG/SVG icons:
<link rel="icon" href="/icon.png"> - Apple touch icons:
<link rel="apple-touch-icon" href="/apple-icon.png"> - Shortcut icons:
<link rel="shortcut icon" href="/favicon.ico">
Not Automatically Supported:
- Dynamically generated favicons (via plugins like RealFaviconGenerator)
- Progressive Web App manifests (
manifest.jsonwith icon arrays) - Favicon sets generated at runtime
Manual Fix for Dynamic/Complex Favicons:
If you use a plugin that generates favicons dynamically:
- Generate your static site normally
- Locate your favicon files (usually in
/wp-content/uploads/or theme directory) - Manually copy them to
/wp-content/cache/stcw_static/assets/ - If needed, update the HTML in your static files to reference the correct paths
Example:
# Copy dynamically generated favicons
cp /wp-content/uploads/fbrfg/* /wp-content/cache/stcw_static/assets/
# Re-create ZIP with updated favicons
wp scw zipSimple Solution: For maximum compatibility, place a standard favicon.ico file in your WordPress root directory. Browsers will request it automatically even without a <link> tag.
- ❌ External CDN assets (Google Fonts, jQuery CDN, etc.) - Links preserved as-is
- ❌ Third-party embeds (YouTube®, Twitter®, etc.) - Require internet connection
- ❌ Dynamic content loaded via AJAX/REST API
- ❌ Assets from different domains (cross-origin)
wp scw enable
# Browse all documentation pages
wp scw process
wp scw zip --output=/docs/offline-docs.zipExport a complete static version for clients who don't need WordPress:
wp scw enable
# Browse site
wp scw zip
# Deliver ZIP filewp scw enable
# Crawl entire site with wget or similar
wp scw process
wp scw zip --output=/backups/pre-redesign.zip
wp scw disableDeploy the static site to any web server without WordPress dependencies:
wp scw enable
wp scw process
wp scw zip
# Extract and upload to Amazon S3®, Netlify®, GitHub Pages®, etc.- PHP: 7.4 or higher
- WordPress: 5.0 or higher
- PHP Extensions:
ZipArchive(for ZIP export)curlorallow_url_fopen(for asset downloads)
- Disk Space: Varies based on site size (static files = ~1.5x site size)
- Permissions: Write access to
wp-content/cache/
# Install PHP ZIP extension
sudo apt-get install php-zip
# Set proper permissions
sudo chown -R www-data:www-data /var/www/html/wp-content/cache
sudo chmod -R 755 /var/www/html/wp-content/cache
# Verify
wp scw statusCheck if generation is enabled:
wp scw statusVerify directory permissions:
ls -la /var/www/html/wp-content/cache/Enable WordPress debugging:
// In wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);Process manually:
wp scw processCheck error logs:
tail -f /var/www/html/wp-content/debug.logVerify external URL access:
curl -I https://your-site.com/wp-content/themes/your-theme/style.cssCheck ZipArchive availability:
php -m | grep zipInstall if missing:
sudo apt-get install php-zip
sudo systemctl restart php7.4-fpm nginxCheck if favicon is in HTML:
# View page source and look for
curl https://your-site.com | grep faviconManually copy favicon files:
# If using a favicon generator plugin
cp /wp-content/uploads/fbrfg/* /wp-content/cache/stcw_static/assets/Use a simple favicon.ico:
# Place in WordPress root - works without tag
cp favicon.ico /var/www/html/favicon.ico- Generation overhead: ~50-100ms per page (buffering + file write)
- Asset processing: Background queue, no user-facing delay
- Memory usage: ~2MB additional per request
- Disk I/O: Sequential writes, minimal impact
The plugin stores static files in wp-content/cache/stcw_static/. Disk usage varies based on your site:
Typical Disk Usage:
- Small site (10-50 pages): 50-200 MB
- Medium site (100-500 pages): 200 MB - 1 GB
- Large site (1000+ pages): 1-5 GB
- Very large site (5000+ pages): 5-20 GB
What Uses Space:
- HTML files: Minimal (typically 10-50 KB per page)
- CSS/JS files: 100 KB - 5 MB total
- Images: Largest contributor (depends on image optimization)
- Fonts: 100 KB - 2 MB per font family
The plugin does not enforce disk limits - disk space management is handled by your hosting environment.
Monitor disk usage through:
- WordPress admin dashboard: Settings > Static Cache (shows total size)
- WP-CLI:
wp scw status(detailed breakdown) - Your hosting control panel
To reduce disk usage:
# Clear all static files
wp scw clear
# Or manually delete old files
rm -rf /var/www/html/wp-content/cache/stcw_static/*Exclude large pages from generation:
add_filter('stcw_should_generate', function($should, $url) {
// Don't generate static files for media-heavy pages
if (strpos($url, '/gallery/') !== false) {
return false;
}
return $should;
}, 10, 2);- Process assets during off-peak hours:
# Add to crontab
0 2 * * * /usr/bin/wp scw process --path=/var/www/html- Disable on high-traffic pages:
add_filter('stcw_should_generate', function($should, $url) {
return !is_front_page() && $should;
}, 10, 2);- Clear old static files regularly:
# Weekly cleanup
0 3 * * 0 /usr/bin/wp scw clear --path=/var/www/htmlBest practices:
- Optimize images before generating static site (use image optimization plugins)
- Clear old static files before regenerating
- Monitor disk usage regularly
- Consider hosting environment with adequate disk space for your needs
Video and Audio Files:
The plugin intentionally does not download video (MP4, WebM) or audio (MP3, WAV) files. These should remain on your WordPress server or external hosting (YouTube, CDN). The static HTML will link to these resources.
Version 2.0.6 includes full multisite support with isolated storage per site:
Storage Structure:
wp-content/cache/stcw_static/
├── site-1/ # Main site (blog_id 1)
│ ├── index.html
│ └── assets/
├── site-2/ # Blog ID 2
│ ├── index.html
│ └── assets/
└── site-3/ # Blog ID 3
├── index.html
└── assets/
Each site maintains:
- Separate static file directories
- Independent asset storage
- Isolated plugin options
- Site-specific WP-CLI commands
Using WP-CLI with Multisite:
# Operate on specific site
wp scw enable --url=site2.example.com
# Or use --url parameter
wp scw status --url=site2.example.com
# Process all sites (loop through)
for site in $(wp site list --field=url); do
wp scw enable --url=$site
doneContributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
git clone https://github.com/derickschaefer/static-cache-wrangler.git
cd static-cache-wrangler
composer install # If you add Composer dependencies later- Follow WordPress Coding Standards
- Comment all classes and methods
- Use type hints where possible
- Write descriptive commit messages
This plugin is licensed under the GPL v2 or later.
Copyright (C) 2025 Derick Schaefer
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
All product names, logos, and brands referenced in this plugin and its documentation are property of their respective owners.
NGINX® is a registered trademark of F5, Inc.
Amazon S3® and Route 53™ are trademarks of Amazon Technologies, Inc.
Netlify® is a registered trademark of Netlify, Inc.
Cloudflare® and Cloudflare Pages™ are trademarks of Cloudflare, Inc.
WP-CLI® and Gutenberg® are trademarks of the WordPress Foundation.
Elementor® is a registered trademark of Elementor Ltd.
Divi® is a registered trademark of Elegant Themes, Inc.
GitHub® is a registered trademark of GitHub, Inc.
YouTube® is a registered trademark of Google LLC.
Twitter® is a registered trademark of X Corp.
ModernCLI.Dev is owned by Derick Schaefer
This plugin has not been tested with any of the services, platforms, software projects, nor their respective owners.
These names and services are referenced solely as examples of where static cache files might be repurposed, used, uploaded, stored, or transmitted.
This plugin is an independent open-source project and is **not endorsed by, affiliated with, or sponsored by** any of the companies or open-source projects mentioned herein.
Derick Schaefer
- GitHub: @derickschaefer
- Website: ModernCLI.dev
- Issues: GitHub Issues
- Documentation: GitHub Wiki
- Discussions: GitHub Discussions
- Automatic sitemap crawling
- Multi-language support
- Incremental generation (only changed pages)
- URL include/exclude patterns with wildcards
- Built-in search functionality for static sites
- Companion plugin marketplace
- Enhanced performance profiling dashboard
See CHANGELOG.md for complete version history.