Recently I had to convert a website powered by WordPress into a static site, as I wanted to host it in an archived state without having to maintain it on a service that supports all the bells and whistles that WordPress requires. Since the content cannot be updated any more anyway, I felt it would make the most sense to find a way to fully convert the site into a genuinely static version.

In my specific case there is no need for theming support, content and style separation, or anything of the sort. Just simply "rendering" the site out to static HTML files, have path URLs be consistent with what they were previously, and be fully compatible with a static hosting solution (my goal was to host it on Netlify).

What follows is what I did to achieve this. I hope if you are in need of something similar (though hopefully for different reasons), this article might be of some use to you. If you want to skip ahead to the most important bit, I am using Simply Static to generate the static version of the website. Additional steps described here are what I did to ensure the end-result was fully usable and suitable to my specific needs.

What you'll need

  • Access to the wp-admin section of the site in question, with the ability to install WordPress plugins
  • Have enough storage space on your current hosting solution for a 1-1 copy of your site, including whatever video and image files that may be part of your site
  • A hosting solution of choice for the static version – this can be the same exact hosting solution you were already using too, of course.
  • Optionally but probably useful: FTP access or another way to download a potentially large ZIP file
  • Some time.

Step 1: Preparing the site

As the method we'll be using will create a static version exactly of the site as it operates currently, we'll want to make a few changes to the settings to ensure no unneccessary parts are baked in with the end-result. While the exact steps here will vary depending on what kind of plugins you are using, I hope these following examples will give you an idea of what to look for. Make sure you walk through your entire site with this mindset in mind, too, as it's going to be quite easy to miss a thing or two, as I'm sure I have, too.

Disable commenting & signing up

In your wp-admin section, head over to Settings > Discussion and disable Allow people to submit comments on new posts.

Next, go to Pages, select all pages using the top-most checkbox, and in the Bulk actions dropdownbox select Edit and hit Apply. In the form that appears right below now, Do not allow in the Comments dropdown box, and hit Update.

Note: If you have more pages than are displayed at once in this list view, you'll have to repeat this step for each subsequent page, or set the Number of items per page under Screen options (top right hand side of the screen) to something high enough.

Lastly, head on over to Settings > General and uncheck the Anyone can register checkbox.

Moderate queued comments and review existing ones

Regardless of whether or not you've been using anti-spam commenting plugins, it'll be good to head over to the Comments section and reviewing all comments. This includes whatever might be in the Pending queue, as-well as any previously approved comment. Once you have "baked" this website, it will remain this way in perpetuity after all, unless you manually go out editing its HTML of course.

Disable any plugins that are no longer needed

Which plugins you might want to disable depends on your preferences, so it will be good to evaluate each plugins indivually. As with comments, once you have "baked" the website, things will stay as they are unless you manually edit the HTML, so especially plugins that inject themselves into each page might be best left disabled.

Some examples of such plugins might include;

  • Tracking (e.g. Analytics) plugins – Google Analytics, Matomo, etc.
  • Yoast's plugins – these are notorious for injecting all kinds of mess
  • Automatic translation plugins
  • Third party or external commenting services (e.g. Discuss)
  • "Live" sidebar/footer plugins that show content from places like Twitter, Instagram, etc.

As I mentioned, which ones you should disable are up to you. It might be worth it to leave some of these enabled to preserve the full functionality of the site, but in other cases it might be more suitable to disable the WordPress plugin, in favor of a different way to inject similar functionality in the new static version. Your new hosting solution might offer their own perfectly adequate analytics solution that requires no installation, for example.

Depending on the theme you're using, this might be as simple as removing the search field widget from the sidebar or main menu, or if the theme does not offer this option natively, you might have to edit the related theme PHP file to hide it manually.

Depending on the theme used, it might be as simple as removing the "Search" widget from where whichever area it's added to.

Start by checking Appearance > Widgets, and possibly Appearance > Menus. If you see a Search widget, simply open it up and click the Delete button.

The same goes for the Meta widget. If this is added somewhere, it'll be good to remove this as it contains links to the login page, RSS feeds, etc. Since none of these will work or are even needed in the statically generated version of the site, you can simply remove the entire widget.

Remove RSS feeds

By default there does not seem to be a way to remove or hide RSS feed information in WordPress, which seems a bit odd, but I suppose this is not normally something you'd want to do. There are several plugins available that try to achieve this, but these usually seem to focus more on preventing the feeds from actually working, not hiding or removing the feed references in your site's code. So instead, let's remove where they're configured directly in your theme's files.

Head over to Appearance > Theme Editor and find and click on the file called functions.php in the right sidebar. If your theme does not already have a functions.php file, you should create it first, then open it up here. Then, add the following to this file, which I have borrowed from this StackOverflow and this StackExchange answer:


// ...everything else already in this file, if anything

/* Remove RSS and related links from head
 * As described here:
 * and here: */
remove_action( 'wp_head', 'feed_links_extra', 3 ); // Display the links to the extra feeds such as category feeds
remove_action( 'wp_head', 'feed_links', 2 ); // Display the links to the general feeds: Post and Comment Feed
remove_action( 'wp_head', 'rsd_link' ); // Display the link to the Really Simple Discovery service endpoint, EditURI link
remove_action( 'wp_head', 'wlwmanifest_link' ); // Display the link to the Windows Live Writer manifest file.
remove_action( 'wp_head', 'index_rel_link' ); // index link
remove_action( 'wp_head', 'parent_post_rel_link', 10, 0 ); // prev link
remove_action( 'wp_head', 'start_post_rel_link', 10, 0 ); // start link
remove_action( 'wp_head', 'adjacent_posts_rel_link', 10, 0 ); // Display relational links for the posts adjacent to the current post.
remove_action( 'wp_head', 'wp_generator' ); // Display the XHTML generator that is generated on the wp_head hook, WP version
remove_action( 'wp_head', 'rest_output_link_wp_head'); // Outputs the REST API link tag into page header.
remove_action( 'wp_head', 'wp_oembed_add_discovery_links'); // Adds oEmbed discovery links in the website.

Save your changes and head over to your website, refresh, and make sure everything is still working as it should. Now if you take a peek at your Web Inspector, you'll find that the feed links should be gone now. Great!

If there is ever a reason you want or need to spin up a dynamic version of the site again, it'll be good to have a full database dump. Alternative a full export from within WordPress could achieve a similar result, of course. I personally prefer to use both, actually, as the built-in export functionality might not include everything (especially certain more advanced plugins), so having a full database backup never really hurts.

You can use your favorite backup tool to achieve this, check with your current hosting provider to find out what options they have available. Some of the cheaper shared hosting solutions oftend have something like phpMyAdmin running, which might be a good enough way in a pinch to get a backup.

Following along the same lines as doing a full database backup, I recommend you also do a full download of your entire site as-is, and backing that up with your favorite backup solution. This will ensure you'll be able to (locally or otherwise) deploy the site exactly as it was together with the aforementioned database backup, without any missing files or potential version hunting needs.

Install "Simply Static" WordPress plugin

Using either the built-in plugin install option, or by manually uploading it, install the Simply Static plugin. This is the plugin that will help us get a fully statically generated version of the site.

Once installed, enable it and you'll find a new main menu item called Simply Static. Head on over there, and first head to Settings to check if everything is set up to suit your needs.

In my case I left Destination URLs to Use relative URLs (/), as I wanted to have the site working on a different (test) domain too. If you are not planning to host the site at all, the Save for offline use option might be useful to you.

As for Delivery Method, depending on your hosting solution they might enforce a execution timeout, which might result in creating of a ZIP Archive to actually fail. This is true especially if your website contains a great deal of larger files (ie. images or videos). You can try this option first, but should it fail, Local Directory will be a viable option too, as you can simply download the entire directory using an FTP client of choice.

You should now be ready to generate your first static version of the site.

Generate the static version of your site

Go back to Simply Static > Generate, and hit the big Generate Static Files button at the top. This will take a while depending on how big your website is, so go ahead and grab a cup of coffee or tea. The page will update as progress is made, until it finally completes and shows a full table of all paths it has exported, along with some additional stats at the top.

If you chose the Zip archive option, it will show a download link at the top too (as shown above), though this link might not allow you to fully download the file successfully due to the aforementioned execution timeouts, as this is not a direct file link but is instead piped through php. You can give it a try (especially if your site is particularly small or you simply don't have large media attached), or you can use your favorite FTP client to download the file directly. You'll find the ZIP file within the static files folder found within wp-content/plugins/simply-static/.

If you had chosen the Local Directory option, head over to the same directory (or whatever you had specified as your custom local directy path in Simply Static's Settings view), and download all files from there.

Gotcha: Mixed content issues

If your site was previously not hosted using SSL but will be on the new host, you might run into mixed content issues. If you have the possibility to set up SSL on your current site before making the switch, it might be worth to set this up and then export the site, so that all externally hosted files (ie. Gravatar-hosted avatars) use the appropriate URL.

However, as there might also be content directly linked to from your blog posts or pages, it might be worth running a find-and-replace on your statically generated site files instead. I recommend doing this after committing a "clean" generated version to your versioning tool of choice, so that you can undo any potential issues that might arise.  

Here's a one-line terminal command that might already give you the results you're looking for. The biggest warning here is that this will update anything, whether it's an image source (which is what we want) or a hyperlink (which might not work, depending on the site being linked to). Manual verification is highly encouraged; don't blindly go for this as it will most likely come with surprises.

find . -type f -name '*.html' -exec sed -i 's/http\:\/\//https\:\/\//g' {} \;

What this command does is find all files ending with .html, and uses the sed inline text editor utility to replace http:// with https://. You can switch this around if you're attempting to achieve the opposite effect, of course.

Gotcha: Files larger than 100mb

In case you want to host the source files on Github (or equivalent), you might run into an issue with limited maximum file sizes. In that case you might want to consider several options. One such solution would be to re-encode an oversized video file to a more reasonable codec, bitrate or resolution. This might be a great choice if your original video file uses a more dated codec or compression technology, for example.

Alternatively you could also consider hosting the video file somewhere, and instead of loading a locally hosted version, update the relevant page(s) to use this hosted version instead. You could host these video files on a common video hosting platform like Vimeo or YouTube, or instead opt to host them on a block storage service like Backblaze B2 or Wasabi, with the latter option perhaps being a better choice for longer-term preservation, as you will not run into potential issues with overzealous and/or abused automated copyright systems, but does come with additional costs you need to factor in.

Deploy to Netlify

You should now have a fully working static copy of your site. What you want to do with it is up to you, of course. In my case I wanted to host it on Netlify, and also preserve its source somewhere. As I already use Github for work and other projects, and because Netlify natively supports deploying from Github-hosted repositories, this was a natural choice for me.

With the static version of the website safely stored in a Github repostiory, deploying is super simple. Just give Netlify access to the repository of choice, set the publish directory in case you placed all statically generated files in a sub-folder (I did this, and called it simply static), and bob's your uncle. Since everything is already statically generated, no build steps or anything are necessary.

Screenshot of the Netlify dashboard showing a new version is being processed.

Next steps

This might not be the very end of the steps necessary. In my case, the website I was preserving has a host of links pointing at other, related sites that were being hosted on sub-domains. In my case as I am working through migrating all these to statically generated, Netlify-hosted variants with potentially different URLs for domains that have since lapsed one at a time, I have to come back and update some of these links to their newly-brought-back-to-life variants. For you it might be fruitful to apply a search through all generated files for links and image paths, so you can update any as needed.

You might also want to modify some personal information that might be present on the site, in case those are no longer applicable or relevant. This, again, depends on your specific needs though, but I thought I'd mention it anyway, as something to think about.

Other than that, with the site ready in its new static form, you would now technically be able to switch over the DNS settings and it's done. Your site is now static, forever unchanging, and perfect for archival and preservation needs.

I hope this might be of some use to you.

Thank you.