We've recently published a plugin for WordPress which generates a static version of your website, syncs it to an AWS S3 bucket and optionally invalidates the CloudFront cache.
In this article we'll lay out our reasoning and introduce the plugin: Simply Static Deploy.
Earlier this year we've explored ways to use WordPress as a headless CMS. This was targeted at extracting content via the WordPress REST API, and compiling a website from there. While we still think this is a great approach for specific use cases, in practice the many steps involved are cumbersome.
We've since then shifted to a much simpler approach: leveraging the power of an existing static site plugin to do the heavy lifting. We've added a deployment layer on top of it, and are quite happy with how things have turned out.
There aren't many static site generator plugins for WordPress. We've found two common static site plugins, namely WP2Static and Simply Static. There are also a few paid static site services for WordPress. We avoided proprietary services to prevent any unnecessary dependencies, and also allowing us to stay more in control of our stack.
WP2Static vs. Simply Static
While testing WP2Static, we've quickly stumbled onto quite a lot of issues when generating a ‘reduced test case website’. It was messing with
srcset attributes, had quite a lot of missing images and assets and generated mysterious unexplainable slugs. After tweaking most options, we still did not get near to a desired end result.
While testing Simply Static, everything worked perfectly without much configuration. We therefore came to the conclusion that Simply Static would better suit our needs.
Simply Static Deploy
While Simply Static generates a static version of a WordPress website, it does not deploy it to a static environment. Our main goal was to offload it from our dynamic web server, so that we could guarantee uptime without the need to scale when we encounter an unexpected increase of traffic. This approach results in lowered hosting costs, guaranteed performance and also a reduced environmental impact.
So we took the Simply Static plugin, and built a wrapper plugin around it. We've added a simple interface, enabling clients to deploy their static website when needed. Our wrapper plugin does the following things:
- Generate: leverage Simply Static to generate a static version of the website (simple HTML files in slug-based folders).
- Sync: deploy the statically created version of the website to an S3 bucket (from Amazon Web Services, or AWS in short).
- Invalidate: optionally invalidate the AWS CloudFront cache.
We haven't published the plugin to the WordPress.org repository (as of yet). However, we've made it available on Packagist. You'll need to leverage the power of Composer to add it to your project. The reasoning is that we're currently aiming at the ‘professional developer segment’, since a static site setup entails quite a bit of necessary knowledge.
This means you'll need to have some sort of ‘modern’ WordPress setup. If you're unfamiliar with Composer, or setups like these, we've written an article about our WordPress setup. A project like Bedrock would also be a good starting point.
To quickly add it to your project, run:
$ composer require grrr-amsterdam/simply-static-deploy
wpackagist-plugin/simply-static. However, we've made a custom fork of it (available as
grrr-amsterdam/simply-static). The original plugin currently doesn't work properly with PHP 7.3+. Our fork contains a fix commit, and we're in the process of trying to get that fix incorporated in the original plugin.
Architecture & Workflow
We deploy our application (WordPress and its theme) to a dynamic web server, hosting the WordPress CMS itself. This is where the client adds, edits and previews content. It is never visited by the public, and is not indexable by search engines. See it as a private, low-resource WordPress installation.
When a client is happy with their changes, they can ‘publish’ it to the static environment (the S3 bucket). This involves hitting the Generate & deploy button (see image above), which invokes all steps in sequence.
For certain projects you might want to import data from third parties, and ensure the static site is updated frequently without the need to manually deploy it. We've therefore added a scheduled deploy mechanism via a WP-Cron hook. This ensures a fresh version of the website, with up-to-date data is made public at a time and interval of your choosing.
WordPress itself isn't that performant. The Loop and all the available hookable filters and actions take their toll. This means that generating the static HTML makes up for most of the ‘deploy time’. This of course justifies serving the whole thing as a purely static website.
We've taken a website with roughly 400 pages (spread across multiple post types). Most of them contain pretty long and complex pages, built with lots of ACF Flexible Content blocks. These are the times it takes for the individual tasks to complete:
- Generate: +/- 3,5 minutes
- Sync: 5 seconds
- Invalidate: 1 second
- We're currently using WP Offload Media to offload all media (mainly images) to a separate assets bucket. The advantage is that all media is directly uploaded to a static environment when it's added to the media library. Meaning it doesn't need to be fetched by Simply Static and synced to the ‘static site bucket’. This allows the process of generating the static to solely focus on generating the ‘site’, and not have to worry about thousands of assets. When applying this technique, make sure to exclude the
uploadsfolder in the Simply Static settings, since Simply Static can simply ignore these files.
- Simply Static uses a technique called background processing to generate the static site (via WP Background Processing). While this is great for the plugin itself, there is no callback when it's done. We've added methods to generate and deploy the website programmatically (to deploy it via an automated schedule). We're therefore circumventing the async/background processing, and made it synchronous. In theory generating the static site could take longer than PHP is allowed to execute. We've provided a hook to increase the default PHP
max_execution_timefor this specific task.
- Credential configuration is done via a a constant (
SIMPLY_STATIC_DEPLOY_CONFIG). We encourage you to add these via a dedicated
.env, for example via a package like PHP dotenv.
- The plugin supports the AWS infrastructure. In theory any S3-compliant provider might work (for example DigitalOcean Spaces), but we haven't tested this. Pull requests to support other providers or improve S3-compliant provider configuration are welcome.
We've been using the plugin successfully in recent projects. While it is quite feature complete (for us), we're eager to get feedback. Issues and pull requests are welcome on our GitHub repo, or reach out to us on Twitter!
Deploy away 🚀