A Fast, Static WordPress Blog

Until recently, my blog felt slow although it contained only static content. In this post, I describe how I decreased loading times and the size of this website by

  • using WordPress as a static website generator,
  • not loading unused scripts and fonts, and
  • employing compression and client-side caches.

According to WebPagetest, a Firefox client in Frankurt with a DSL connection and an empty cache needed to download 666 kB (28 requests) and had to wait approximately 7.7 seconds before being able to view my frontpage from April 4. With the static website, the same client has to wait about 3.1 seconds and transfer 165 kB (18 requests). As a side-effect, the website offers considerably less attack surface now and user privacy was improved.


Earlier this year, I was annoyed by the speed of my blog and this feeling was reinforced by mediocre test results on online website speed tests like PageSpeed Insights, WebPagetest, or Pingdom Website Speed Test. These tests also highlighted the suboptimal configuration of the HTTP server (no compression, unused client-side caches). Since my WordPress blog is static and since there are usually many vulnerabilities in WordPress, in WordPress plugins, or WordPress themes, I decided to replace my online presence with a static copy of my WordPress blog.

The static website is faster thereby improving user experience and it provides better privacy; given a link to a file, a web browser sends the address of the website containing the link whenever it downloads the linked file (the so-called HTTP referrer). Consequently, HTTP referrers allows user tracking across different websites and by default, WordPress with the Twenty Fourteen theme loads one set of Google webfonts when viewing the page and it loads another set of Google webfonts when logging in allowing Google to distinguish between regular website visitors and authors (see also this link). Except for the blog posts using MathJax, the static website does not require external data for viewing.

For the sake of completeness I want to mention there are also SSL tests for servers, e.g., by Qualys SSL Labs and by wormly.

Preventing Downloads of Unused Content

Since we will disable the comments later, we will prevent web browsers from loading Gravatar data. To that end, the option Show Avatars in Settings -> Discussion -> Avatar Display must be unchecked (the discussion settings are not available if the Disable Comments plugin is active). WordPress supports emojis and if you do not use these, then they can be disabled with a plugin called Disable Emojis. Furthermore, WordPress has been loading Google webfonts for some time now and these can be disabled with the plugin Disable Google Fonts.

Making a WordPress Blog Static

In order to use WordPress as a static website generator, there must be no dynamic content unless the user is logged in. Consequently, the WordPress blog must

  • disable comments,
  • avoid attachments pages,
  • disable shortlinks, and
  • make the search function inaccessible.

Comments can be globally disabled by the plugin Disable Comments. Links to attachment pages have to be removed manually from every article whereas shortlinks can be disabled with a plugin or by adding the following line to the file functions.php in the active theme:

remove_action( 'wp_head', 'wp_shortlink_wp_head' );

Here, we want to remind the reader that the preferred way to modify standard themes is by means of child themes. Finally, search forms can be removed by customizing the current WordPress theme; the 404 page may contain a search form, too.

Hosting WordPress Yourself

Since I want to have my website serve only static content, the WordPress blog needs to move somewhere and I decided to host WordPress offline on my own computer. Hence, I needed to install a LAMP stack or a similar solution stack on my machine, install WordPress, and move the blog afterwards.

I chose a vanilla LAMP stack and I struggled with the setup of the PHP interpreter because there are at least three popular ways to have Apache run PHP code. The how-to by Nathan Zachary describes a state-of-the-art solution using FastCGI over UNIX domain sockets.

I followed the reference guide for installing WordPress. The guide glosses over one question: who owns the directory with the WordPress installation? The PHP interpreter needs to have read and write access to the wp-content folder in the WordPress directory because this is the location where WordPress stores its plugins, themes, and uploaded files so on my computer

  • the PHP interpreter called by Apache (php-fpm) is executed as user php-fpm,
  • the subdirectory wp-content is owned by the user php-fpm and the group root,
  • every other file and directory of the WordPress installation is owned by root¬†(user and group).

Moving a WordPress blog involves

  • creating a backup of the online WordPress blog,
  • importing the backup into the offline WordPress blog, and
  • replacing the links to the online blog.

Backups can be created manually or you can use WordPress plugins; I used BackUpWordPress for exporting the blog and I manually imported the data on my computer with the aid of the WordPress' how-to. After importing the WordPress data, there may still be links to the online website in the blog posts and these need to be updated. Furthermore, if WordPress is hosted offline, then it makes sense to disable automatic notification of blog update services (Settings -> Writing) and search engines.

Generating a Static Website

Given a WordPress blog without dynamic content (when not logged in), the goal is to generate a snapshot of the blog and make it available online. Initially, I used the WordPress plugin Simply Static to generate static copies but later I wanted a fully automatic process for generating snapshots and updating the online website. The automated process has to

  • download all pages reachable from the front page,
  • download the 404 error page,
  • optionally copy other files like the site map, robots.txt, or stylesheets for the XML site maps,
  • remove query parameters from filenames,
  • replace the links to your offline WordPress blog with links to the online website, and
  • upload the static website via SSH.

This can be done with standard UNIX tools and a fully automated script can be found in my git repository. The script does not check for dead links but there are online services for this purpose.

Web Server Settings

Even for completely static websites, HTTP server settings still have a large impact on website performance. I followed the proposals by the online speed tests mentioned above and enabled

  • ETags,
  • client-side caching, and
  • compression.

Moreover, without WordPress and its plugins, I needed to redirect HTTP connections to HTTPS and canonicalize URLs manually. A corresponding .htaccess file can be found in my git repository (this file does not enable ETags as they were enabled by default at my webhoster).