{"id":1161,"date":"2020-03-29T20:02:07","date_gmt":"2020-03-29T18:02:07","guid":{"rendered":"https:\/\/craftcoders.app\/?p=1161"},"modified":"2024-08-14T14:27:51","modified_gmt":"2024-08-14T12:27:51","slug":"wordpress-vs-static-web-pages-the-best-of-both-worlds","status":"publish","type":"post","link":"https:\/\/craftcoders.app\/wordpress-vs-static-web-pages-the-best-of-both-worlds\/","title":{"rendered":"WordPress without the security hassles \u2013 using GitLab CI\/CD to automate transforming WordPress into a static website"},"content":{"rendered":"\r\n

Recently we launched our new company website (craftcoders.app<\/a>). It\u2019s a simple website that showcases us and our work and describes the kind of services that we provide to customers. It requires no dynamic features except for the contact form.<\/p>\r\n

We decided to build our website with WordPress, but to automatically generate a static copy of it and serve that to visitors. We’re using Gitlab CI\/CD as automation tool. This guide will explain how you can setup your own pipeline to generate a static website from a WordPress site on a regular schedule or manually. But first we’ll have a detailed look at the pros and cons of WordPress and static websites in the next section. Feel free to skip over it, if this is not of interest to you.<\/p>\r\n

The ups and downs of WordPress and static websites<\/h2>\r\n

At craft-coders we value efficiency, and we try to choose the right tool for the job. WordPress is the leading CMS for small websites. It\u2019s easy to set up and deploy. At the time of writing ~35% of all websites on the internet are built with it. Because of its popularity there are tons of useful plugins and great themes available. So that you can build good-looking and feature rich websites really quickly.<\/p>\r\n

But WordPress has its downsides. Mainly it sucks at security. So famously, that ~1\/5 of the Wikipedia<\/a> article on it focuses on its vulnerabilities. The plugin market for WordPress does not provide any quality checks and if you look at the code base of most plugins (even some popular ones), any self-respecting programmer will scream out in agony.<\/p>\r\n

Because of this we are very much against using WordPress for more than simple representational websites and blogs. Basically if your website is built on WordPress you must expect getting hacked. It’s therefore crucial that your WordPress installation is running on a server that isn\u2019t storing any sensitive information about you or your customers and that you use passwords that are used nowhere else. If people really need to log into your website, then at best you use an external authentication service, so that no information about passwords is stored on your server.<\/p>\r\n

Still, even if there is nothing of value to gain for a potential attacker, so that a targeted attack against your website is very unlikely and getting hacked is more a nuisance than an actual problem, you still need to take basic precautions. Due to the popularity of WordPress there are a lot of bots out there that just scan the web for known vulnerabilities. They try to hack as many web pages as possible and use them to spread SPAM emails, viruses and propaganda, or use your server to mine Bitcoins.<\/p>\r\n

The most important thing that you must do to protect yourself from bots is to keep your WordPress installation and its plugins updated at all times. This can be very annoying because updates may break things. And for most small websites the goal is often to deploy and forget. You don\u2019t want to spend time fostering your site, but just want it to continue to function as expected and be done with it. The ultimate goal of every person in operations is, to go unnoticed. If you have an admin that is constantly running around fixing stuff he\/she is probably not doing a good job, or he\/she has to compensate for the mistakes of the developers. You want things to work without the need of thinking about it.<\/p>\r\n

Even though WordPress is the nightmare of every admin, in contrast to that, static web pages are the dream of every person working in operations. They\u2019re super easy to deploy, work super fast, can be kept in RAM and requests can be distributed between as many servers as you like. Because there is no code running on the server involved, they are basically unhackable. Provided of course that your webserver is secure, but since you can just rent a managed server this isn\u2019t really an issue that you need to concern yourself with. Yes, attacks running in the clients browser exploiting flaws in JavaScript or CSS are still feasible, but since a truly static website by definition has no valuable cookies or private information to steal, there is little to be gained by performing an attack in this manner (talking to authenticated REST-Service can change that picture of course).<\/p>\r\n

There are a few good static site generators out there, but as of now no one of them provides an easy-to-use GUI and as many plugins\/themes as WordPress. If your goal is to build a simple website fast, WordPress should still be your first choice. Also if you decide to go with a static site generator there is no going back, your site will forever be static. Of course, you\u2019re always free to use JavaScript to talk to REST-services and that is a good design choice, so this sounds more dramatic than it actually is.<\/p>\r\n

To sum it up WordPress is great for editors and site-builders but it sucks in operations. In contrast, static web pages are hard to use by editors and usually require more development effort than WordPress, but they are great in operations. This is a classic development vs. operations issue.<\/p>\r\n

Using WordPress to generate a static web page<\/h2>\r\n

What if you could have both? Why not have a private non-accessible installation of WordPress and from that generate a static copy. Then you can deploy that copy to a public accessible web space. That way you have the best of both worlds. Of course you deprive yourself of the dynamic features of WordPress, so no comment fields and no login sections, but if you don\u2019t need any of that, this is a perfect solution for you. And if your requirements ever change you can always replace your static copy with the real thing and go on with it.<\/p>\r\n

This is the basic idea. The first thing we tried out was the WP2Static plugin which aims at solving this issue, but we couldn\u2019t get it running. We then decided to build our own solution using our favorite automation tool GitLab CI\/CD<\/a>. We used gitlab.com, and at the moment they are offering 2000 free ci minutes to every customer, which is a really sweet deal. But any ci-tool should do. You should not have many issues porting this guide to Jenkins or any other tool that allows to execute bash scripts. Also, we’re assuming you are using Apache (with mod_rewrite) as web server and that you can use .htaccess files. But porting this concept to other web servers shouldn\u2019t be too difficult.<\/p>\r\n

\"\"<\/a><\/p>\r\n\r\n\r\n\r\n

You can find and fork the complete sample code here: https:\/\/gitlab.com\/sgellweiler\/demo-wordpress-static-copy.<\/a><\/p>\r\n

Here is the plan in detail. We\u2019re going to use the same domain and web space to host both the private WordPress installation and the public accessible static copy. We\u2019re going to install WordPress to a sub directory, that we will protect with basic auth using a .htaccess file. This is the directory that all your editors, admins and developers will access. The Gitlab job will crawl this installation using Wget and deploy the static copy via ssh+rsync into the directory \/static on the web space. Then will use the .htaccess file in the root directory to rewrite all requests to the root path into the static directory. You can configure the gitlab job to run every day, hour or only manually depending on your needs.<\/p>\r\n

To follow this guide you should have access to a *NIX shell and have the basic Apache tools (htpasswd), ssh tools (ssh-keygen, ssh-keyscan), find, sed and GNU Wget installed. Some distros ship with a minimal Wget installed, so make sure that you have the feature rich version of Wget installed (wget –version).<\/p>\r\n

Setting up the web space<\/h3>\r\n

First install WordPress into a sub directory. For this guide I\u2019m going with wp_2789218<\/em>. You can go along with this name or choose your own, you should use a unique name tough, a string that you will use nowhere else. Best you add a few random generated chars in there. We\u2019re not doing this for security but to make search+replace for urls easier in the next step. If you go with your own folder name remember to replace all occurrences of wp_2789218 <\/em>in this guide with your folder name. We\u2019ll also add a catchy alias \/wp<\/em>, for you and your coworkers to remember, so don’t worry too much about the cryptic name.<\/p>\r\n

Next we create a directory to store our static copy. We\u2019ll just name that static\/<\/em> and for now we\u2019ll just add an index.html<\/em> with <h1>Hello World<\/h1><\/em> in there.<\/p>\r\n

Let\u2019s configure Apache to password protect our WordPress installation and to redirect request to \/static<\/em>. First generate a .htpasswd<\/em> file with user+password at the root-level (or at another place) of your web space using:<\/p>\r\n

htpasswd -c \/home\/pwww\/.htpasswd yourusername<\/code><\/pre>\r\n

Next create a .htaccess<\/em> on the root level with the following. You need to reference the .htpasswd<\/em> file with an absolute path<\/strong> in the AuthUserFile<\/em>:<\/p>\r\n

RewriteEngine On\r\nRewriteBase \/\r\n\r\n# Setup basic auth\r\nAuthUserFile \/var\/www\/httpdocs\/.htpasswd\r\nAuthType Basic\r\nAuthName \"Only for trusted employees\"\r\n\r\n# Require a password for the wp installation.\r\n<RequireAny>\r\n    Require expr %{REQUEST_URI} !~ m#^\/wp_2789218#\r\n    Require valid-user\r\n<\/RequireAny>\r\n\r\n# Add an easy to remember alias for the wp installation.\r\nRewriteRule ^wp\/(.*) wp_2789218\/$1 [R=302,L]\r\nRewriteRule ^wp$ wp_2789218\/ [R=302,L]\r\n\r\n# Rewrite all request to the static directory.\r\n# Except for requests to the wp installation.\r\nRewriteCond %{REQUEST_URI} !^\/static.*\r\nRewriteCond %{REQUEST_URI} !^\/wp_2789218.*\r\nRewriteRule ^(.*)$ static\/$1 [L]\r\n<\/code><\/pre>\r\n

And that\u2019s it for the server config part. If you go to your.domain.tld<\/em> then you should see the Hello World<\/em> from the index.html<\/em> in the static directory. If you go to your.domain.tld\/wp<\/em> you should get redirected to your WordPress installation and be forced to enter a password.<\/p>\r\n

Generating a static copy of a website<\/h3>\r\n

To make a static copy of your website you need a crawler that will start at your start page, follow all links to sub pages and download them as html including all CSS and JavaScript. We tried out several tools and the one that performed the best by far is the good old GNU Wget. It will reliably download all HTML, CSS, JS and IMG resources. But it will not execute JavaScript and therefore fail to detect links generated through JavaScript. In this case you might run into problems. However, most simple WordPress sites should be fine from the get go.<\/p>\r\n

Let\u2019s have a look at the Wget cmd we will use to generate a static copy of our WordPress site:<\/p>\r\n

wget \\\r\n    -e robots=off \\\r\n    --recursive \\\r\n    -l inf \\\r\n    --page-requisites \\\r\n    --convert-links \\\r\n    --restrict-file-names=windows \\\r\n    --trust-server-names \\\r\n    --adjust-extension \\\r\n    --no-host-directories \\\r\n    --http-user=\"${HTTP_USER}\" \\\r\n    --http-password=\"${HTTP_PASSWORD}\" \\\r\n    \"https:\/\/yourdomain.tld\/wp_2789218\/\" \\\r\n    \"https:\/\/yourdomain.tld\/wp_2789218\/robots.txt\"\r\n<\/code><\/pre>\r\n

Here is an explanation of all the options in use:<\/p>\r\n