In this tutorial, you will learn how to host a high traffic serverless WordPress website for a total of $13.16 yearly.
That’s too good to be true, what’s the catch?Skeptic Developers
It’s serverless static site, which means its setup might be complicated, WordPress Search function won’t work*, and there are a few extra steps you have to do before publishing a new post.
*We will discuss an alternative search services in Step 6.
Why should I go serverless then?Rational Thinkers
If your WordPress website generates a lot of traffic and you care about load time, a static site will be more scalable then a regular dynamic WordPress site. Other reasons to switch:
- Increased security: Everything is static, there are no worries about SQL injections, WordPress Vulnerabilities(Ex: Unserialization issue), Authentication Bypass flaws in bad plug-ins, etc.
- More affordable: Static web-hosting in general is cheaper than dynamic. There are great services like GitHub Pages, Netlify, and Surge that will give you a generous amount of bandwith for free!
- Speed: You can take advantage of caching all your content and serving through Cloud Flare, since you know everything is static anyway. If you follow this tutorial and use Netlify, there is no need for Cloudflare because JAMstack.
We’ll use Netlify, WordPress, Uniform Server, and HTTrack, and NameCheap. Netlify will be our host. Uniform Server will be our local server. HTTrack will be our static site generator. NameCheap will be our domain register. You can skip the NameCheap step if you already have a domain name or want to use another domain register.
Step 1: Downloading and Installing WordPress Locally
If you have an existing WordPress site already running on a server or
locally that you want to staticify, skip to Step 3. Otherwise, we need
to install WordPress, which requires you to run PHP, MySQL, and
Apache/NGINX on your machine.
We are going to downloading Uniform Server because it nicely packages PHP, MySQL, and Apache . I prefer Uniform Server over XAMPP and WampServer, but if you already have those local servers running you can use them instead. Open the executable called UniController.exe and start Apache and MySQL. Linux users: I’d recommend installing PHP, Apache, and MySQL without these pre-bundled packages because it’s the Linux way.
Now, download WordPress and Extract it to the “www” sub-folder in UniServerZ directory.
In the UniServer Zero XIII window, click
MySQL->Database create-delete. Create a database name(Ex: WordPress). Now click
MySQL->create restricted MySQL user. Give it a username,password, and point it to the database you created. Now go to
localhost/wordpress/ in your browser and provide WordPress with the MySQL credentials you just created.
Step 2: Add themes, post, plugin, etc.
I’m assuming you don’t need a guide on this part. When your pleased with your website move on to the next step. Recommendation: Disable Font-Awesome and Install Webcraftic Clearfy to disable search, comments, etc.
Fix: “Installation failed: Download failed. No working transports found”
Step 3: Creating a static version of WordPress
Download and Install HTTRack. Although HTTrack looks like it’s from the 90s, I prefer it over new tools like Scrapy and Hamster. If your feeling adventurous you can try wget, but I won’t get into that. Getshifter.io provides a convenient static WordPress website, but we won’t use them because they currently charge $20/month for using a custom domain name!In the WinHTTrack Website Copier window, click
File->New Project. Click
Next. Give the project a name and path to save the static website to. Add the Website URL (Ex:
Step 3B: Fixing WordPress Absolute URLs
WordPress uses absolute URLs, so all
http://localhost/wordpress within links has to be changed to
https://yourdomain.com . Update line 47 to point to your desired domain,
Install Python’s htmlmin package if you want to compress the HTML or delete lines 49 to 76 and remove htmlmin from line 1:
pip install htmlmin.
Run the script in the static website directory.
import os,errno,shutil,glob,codecs; # Without htmlmin import # Delete in Background def trmSilentDelete(filename): try: os.remove(filename) except OSError as e: if e.errno != errno.ENOENT: #No Raise Exception if no such file or directory raise # Raise Exception other Error # Replace Content in File With def trmReplaceWith(filename,filetype,old_text,new_text): # Create Temp File with Replaced text fA = codecs.open(filename + filetype, 'r',"utf-8") fB = codecs.open(filename + '_temp'+filetype, 'w',"utf-8") for line in fA: fB.write(line.replace(old_text, new_text)) fA.close() fB.close() # Copy Temp to Orignal shutil.copy(filename + '_temp'+filetype, filename +filetype) # Delete Temp trmSilentDelete(filename + '_temp'+filetype) # trmReplaceWith for Looped for a folder def trmReplaceFolder(filetype,old_text,new_text,foldersub=""): lsfiles = glob.glob(foldersub+"*" + filetype) # Replace Que for replaceque in lsfiles: trmReplaceWith(replaceque[:-len(filetype)],filetype,old_text,new_text) # trmReplaceFolder for every folder def trmReplaceAllfolder(filetype,old_text,new_text,walk_dir='.',recursive=True): allSubFolders=next(os.walk(walk_dir)) print(allSubFolders);# Print current folder for debugging crashes for foldersub in allSubFolders: print("Currently in "+foldersub); trmReplaceFolder(filetype, old_text, new_text,walk_dir+"/" +foldersub+"/"); if (recursive==True): trmReplaceAllfolder(filetype,old_text,new_text,walk_dir+"/" +foldersub); if walk_dir == '.': trmReplaceFolder(filetype, old_text, new_text) #Removes Extra Index Files Generated by WordPress garbageHTMLSearch = glob.glob("*.html"); for WebHTMLFile in garbageHTMLSearch: if (("index" in WebHTMLFile) and (len(WebHTMLFile)>10)): trmSilentDelete(WebHTMLFile); # Fix Host trmReplaceAllfolder(".html",'localhost/wordpress','www.RonFoods.com'); # LINES 49 to 76 Deleted
Step 4: Pushing static files to a GitHub or GitLab
If you don’t have a GitHub or GitLab account sign up for one. I’m using GitHub. Create a new repository for your website. Install Git on your computer and configure your global user.name and user.email
Go to localhost\wordpress folder that was created in step 2 and run the following commands in CMD.
git add .
git commit -m "first commit"
git remote add origin https://github.com/YOURUSERNAME/YOURREPONAME.git
git push -u origin master
Step 5: Publishing the Website
Create a Netlify Account using your Git account. Click,
New Site from Git->
Your Git Repo(Step 4)->
Deploy Site. Now, click
Set up a custom domain. This is the step where you get out you wallet and spend around $8 to $15 per year for your custom domain name. RonFoods.com cost me $13.16 to renew this year on NameCheap.
Step 6 (Optional): Replacing WordPress Comments, Search, WooCommerce, and Tracking with alternatives.
Since WordPress Comments, Search, WooCommerce rely on MySQL custom dynamic MySQL queries, we will have to find 3rd party solutions to replace these features. Fortunately, there are many 3rd parties that provide great products without a huge increase on load time all over GitHub. Below is a list of various APIs, services, and libraries to add to your static WordPress website.
- Various ways to include comments on your static site
- Personally like GraphComment
- Algolia Search
- I’d argue this is the best search as a service(SaaS) you can get.
- How to Build Ecommerce Websites in 2019
- Try Snipcart. I never used it. Personally, this is the only part I am not a fan of JAMstack philosophy on. Maybe, RonFoods.com will migrate to Snipcart someday.
- Automates the process, but I personally don’t want to pay $20/month for a custom domain.
- How I Migrated From WordPress to a Static Site
- Maybe, you want to get rid of WordPress all together. There is certainly a rise in JAMstack believers and some awesome sites on Netlify that use Hugo, like our very own DroneIt website.
Final WordPress Static Website
- Total Cost: $13.16/yr
|Payment Provider||2.9 % + $.30/transaction||Stripe**|
|MySQL Alternative||$0.00||AirTable API***|
**I’m not including the payment provider in the cost of the website because it’s a cost that comes from people buying products on our website.
*** Has a 5 request/second limitation, so it’s a good idea to cache request. 1200 Rows per base, but you can have unlimited bases.