a group of people standing next to each other

If you’re looking to expand your web presence outside of social media or need a digital storefront to send new or even old customers, a website is still the best way to showcase your brand and your business. This beast of an article will go through the process of getting you up and running, from the hardware to the software, and go through all the fun quirks, pitfalls, and oopsies I’ve came across over the last few years.

Important Note
I am not a certified web engineer, network engineer, etc. My experience comes from reading online articles and how-to books/pages, and trial and error. If you are needing a managed site with full support, this may not be the article for you. I treat web security very seriously, and do my best to point out good practices for web security, but web design and network security are always changing and my primary career is not IT-based.

Do You Need a Site?

Short answer: it depends on what you want to get out of it. You can set up a store front on social media, pay for adds, and never have a separate presence other than the social media page. I’m personally not a fan of social media, but it is necessary to attract new clients and is a good way to boost growth quickly. The advantage, as I see it, of having a full-fledged site is so that you can introduce your brand, your way. With social media you are constrained by the limitations and terms of the owning group, whether its Meta or something else. With your own site, you get to decide what you want it to look like, the face and tone you want to present, and the mechanics behind it.

Typically, I would approach it this way: use social media to post daily, boost your products, and create a social presence that makes you real. Every social media site has different terms, conditions, and limitations. You can do some things on one platform that you can’t do on another. Your site, on the other hand, will be a consistent place of information and branding. Link back to it. Be real on social media, show a face, and use your site to provide depth and context behind the real you.

At the end of the day, you’re here because you’ve decided this may benefit you. Whether it’s selling a product or simply providing a way to network with family and friends, sit down and figure out what you want to do, before we start digging into the actual setup.

What do you need out of your site?

When you’re thinking about your site, what do you want to get out of it? I run two sites. This one, which is really a place to run both a small mindset/motivational business, and a family site for my extended family. Both operate and function very differently. Let’s look at the family site first as an example.

Just a place to connect

The family site I maintain is there as a place to connect and keep our extended family in the loop with what we are doing across the country. No products are sold, no appointments are scheduled, and the most active member is the bot that does automatic posting of weekly email updates. My family likes to cook, and we like to travel. So…the site, besides having a calendar for the 100+ birthdays and anniversaries we have to keep up, also hosts a recipe collection and photo galleries.

A site for business

On the other hand, we’ve got this site. It actually started as a blog site for me to dump information, but it evolved into more of a site for motivational and mindset coaching, ran by my wife, while I still dump info in the blog side of the site. It serves as a place to offer all of our skills and services, including piano tuning, automation, motivational coaching, and more.

Thanks to plugins, I can invoice clients for piano tunings, automate scheduling and emails, and even have a robust user management system with site-wide messaging. Through integrations with Stripe and Paypal payments are handled through reputable vendors, and I don’t have to worry about the banking side of things (which introduces a whole new level of complexity).

The Necessities

Before we dive in and actually start the process, let’s take a look at what you will be getting into. We’ll also take a look at how the web works (simplified way down). A basic understanding of the why and the how behind the scenes is crucial to understanding how to fix a problem if something doesn’t work as expected. As we build, I’ll explain as we go in each section the why so it sticks.

What You’ll Need

Let’s lay out what you’ll need. We’ll go into each piece in a bit.

  • A domain name (eg., “website101.com”, “webdesign.me”, “websitematters.co”) purchased from a reputable registrar
  • An always-on machine to run the web server and host the files. I do not recommend a PC that is used regularly for non-site activities; a dedicated machine is best.
  • A reliable high-speed internet connection
  • (Optional) Battery backup for your server, router, and modem

What We’re Going to Set Up

So, here’s how this is going to work. You’ll purchase a domain name from the registrar; make sure you like it because you’re stuck with it for at least year, and it is not particularly easy to change later on. With a domain name, you’ll be able to type “mysite.something” into the browser bar and it’ll take you to your site (with a little more setup behind that to make it work).

On your side, we’re going to build a headless LAMP stack. That’s Linux/Apache/MySQL/PHP. Don’t worry if you’ve never used Linux, I’ll explain as we go. The headless part means we’re going to set it up so that you don’t need a keyboard, monitor, and mouse always attached. Displays use power, and if you use a battery backup you want to keep power draw to a minimum. Plus, you really don’t need to use the Linux desktop all that often anyway.

Once the LAMP stack is compiled and all the correct permissions, passwords, and databases are created, we’ll set up the Apache web server, grab a SSL certificate from Let’s Encrypt, and compile WordPress. From there, I’ll walk you through the initial setup process and point out some tips and tricks I’ve learned before turning you loose to get designing.

Why do it this way?

Why self-host? Why WordPress? Simple, it’s free. Other than the domain name and hardware purchase, if you don’t want to put another dime into it you can.

You can, if desired, create an account on WordPress.org and get the same content with a managed, hosted provider, but the power of a self-hosted site lies in the plugins. WordPress.org requires payment to add and use plugins; WordPress plugins allow you to really tailor your site to your needs and style. A self-hosted site can use as many plugins as you need, as long as they are compatible with each other.

Let’s get started.

Purchase and Set Up a Domain

The first step is to claim your web presence. Let’s look at some basics of how the web works to understand how we’ll set this up. I’m going to approach this like I’m talking to my children, who know nothing of how the web works.

Web Basics 101

Every machine attached to the web has an IP address. Most people in charge of the monthly internet payment at home recognize (or That’s usually the default IP address of their home router. They may not realize that their home has its own IP address as well. Think of an IP address like an address you would type into your GPS or Google Maps. You’re going to 123 XYZ Street. That’s the external IP address. Now, specifically, you’re going to 123 XYZ Street, Suite B, Room 4. That’s internal IP addresses and routing. I’ll explain how that works next.

DNS Records and Routing

Okay, so when you want to visit Amazon.com, you don’t type a string of numbers into the bar. Web designers a long time ago realized that people just don’t remember numbers, and nobody can memorize the billions of IP addresses that make up the web. But we can easily remember a name.

So, when you type “amazon.com” into a browser bar, your computer sends out a request that essentially says “hey, who knows the IP address of amazon.com” to the first DNS server. DNS is short for Domain Name System. A DNS server translates IP addresses into well-known names. Each DNS server has a list of the names (or domains) it owns or is familiar with. So, the first DNS server doesn’t know who owns amazon.com. It forwards your request on to another server, and so on, until the request hits a server that says “yeah, I own the record for amazon.com and know the IP address”. At that point, your request is sent to the web server of amazon.com, which serves the page back to your machine.

Once a path to a server is known, it is retained. I.e., the next time you request amazon.com, your path will go directly to the owning DNS server. In other words, the first time you visit an unknown page, it may take a little longer to serve the page as the web finds out the best way to get you to that server.

So what?

The takeaway here is that you will be setting up the correct DNS records with your domain to point the web to your web server so that when Jane types in “mysite.something”, instead of a “404 Not Found” page she sees your pretty landing page.

Choosing a Registrar

When choosing a domain, it’s important to choose a privacy-oriented registrar. There’s a ton of options available, including GoDaddy, NameCheap, NoIP, DyDNS, Wix, WordPress.org, and many others. Realize, though, that when you purchase a domain you must provide your address and contact information to the registrar to be in compliance with global regulations. You want a registrar that won’t sell that info. Personally, I use Cloudflare for my domains. Cloudflare is a CDN (Content Delivery Network), so it actually speeds up and enhances my sites, provides a wide variety of domain names to choose from, stays at the cutting edge of evolving technology, and is privacy-centric.

Another advantage of Cloudflare is that all traffic is proxied through their servers prior to hitting your server. What that means is that, when queried, your site will return Cloudflare’s IP address pool versus your personal home IP address. While there are ways around this if an experienced hacker is trying to expose your IP, it is an added layer of security.

This guide will assume you’ve chosen Cloudflare as your registrar. That being said, the processes are generally the same or similar for any other registrar you choose.

Purchasing Your Domain

Purchasing a domain through Cloudflare is a quick process. Note that certain “high value” domains will cost more. Our domain (themays.me) is a low-value, low-traffic domain that costs ~$20/year.

From the Cloudflare dashboard, hit “Add a Site” and then under the add a domain link hit “Search”. You’ll be searching registered web domains to make sure your domain hasn’t already been taken. Have two or three options ready in case your first choice is already gone.

On the search page enter the domain you would like to register and, if available, hit “Purchase”. You’ll purchase the domain name for any number of years. Provide your information (if a business and you have a business address, provide it), payment, and pay for your domain. Congratulations, you now own a web presence. But we’re not there yet.

That’s step one. Step 2 will point the web to your server, but let’s get that set up first, otherwise you’ll send people to your home router page (which isn’t good).

The LAMP Stack

Buckle up, this section is a bit of a beast. We purchased the domain first because once we set up the LAMP stack we’ll go ahead and set up Apache and your home router to start handling requests. By having a domain name already secured, we don’t have to go back and change things if, by chance, the domain name had already been taken. Let’s get started.


Hardware Requirements

Probably the largest up-front cost will be the hardware. If you use plugins, those may have costs for the “premium” versions, but we’ll get into that later. You will need an always-on machine. This can be a NAS (network-attached storage, i.e., Synology, QNAP), Raspberry Pi, desktop, mini-PC, etc. Ebay often has refurbished mini-PCs for sale at very reasonable prices ($70-$100) that come with Window pre-installed. This guide will go that route, and I’ll walk through removing Windows (as best I can remember). Note that for the first couple of years I used a Raspberry Pi to host this site and had no issues.

Security Thought
I do not recommend a personal PC as the always-on server. You will be exposing this machine to web traffic. To think about it practically from the analogy of your home, you’ll be leaving your front door open, and unlocked, 24/7 to anybody and everybody that wants to visit. Tech-savvy peeps can poke around in your home, check out the drawers and closets, and make themselves at home. You want to digitally separate this machine from everything else. Cloudflare will act as the “bouncer” and relay information, while we’ll have the server locked down in an outbuilding, with nothing else. All that anyone will see and access will be what you want them to see.

You’re also going to need for initial setup a display, mouse, and keyboard that connects to the mini-PC as well as a USB thumb drive with enough space to store the Linux ISO image we’re going to create.

Optional Hardware

Optionally, place all of your internet essentials and the server on a battery backup. Optional, but highly recommended. If your power goes out, so does your web presence. You can usually get a decent APC or CyberPower battery backup for $150-$200. A mini-PC typically draws very little power, along with the modem and router, so runtime will usually be at least an hour or two depending on the size of the UPS you purchase. As an example, I’ve got a 1500VA/900W backup running 3 mini-PCs, a NAS, router, modem, 2 switches, NVR, 4 Raspberry Pis, and a few other components and can get ~30 minutes runtime during power outages. Not much, but we don’t usually lose power, so it gets me through any occasional loss or brownout.

Software Requirements

Let’s look real quick at the software we’ll be installing. Most of these will be high-level intros; if more detail is required, we’ll get into that during the actual installation.


We’re going to replace any existing operating system on the mini PC with Linux. Linux is a light weight, free OS that works particularly well for headless installs. If you’ve never used Linux before, it takes a little getting used to. Windows users will probably remember the command prompt. Linux works best from the command line, and that’s where all of the install will take place.

If you want a pretty detailed intro to Linux, you can check out the official homepage at Linux.org. I picked up a book from our bookstore for a physical command reference as well. Something you’ll need to wrap your head around (if a newbie to Linux) is permissions. With Windows, you have to provide “Administrator” permissions to access or do certain programs or actions. With Linux, you will have to become comfortable with “sudo”, or super user privileges. Processes, such as Apache, have their own user permissions. If not configured correctly, those processes will not be able to complete their functions because they will not have access to the directories they need to properly work. As we move through the guide, I’ll explain what these commands are, and the why behind them, as we go.

No “Undo”
A key difference with Linux is that there is no undo. Once you hit enter, that command executes if properly built. Case in point: I had properly built a command that, while not intended, completely removed all root directories on the web server once. I hit enter, and it was all gone. No stops, no “are you sure you want to do this”, just gone. Thankfully…I had a backup stored elsewhere.

Apache is the primary web server. Essentially, Apache will tell incoming requests where to find the files needed to display your site, how to direct requests, and how to handle errors. Think of it like a site supervisor. It doesn’t perform the actual work, but it does give direction and a framework for how to handle requests and problems.

MySQL (Maria DB)

We’ll be using Maria DB as the data repository. Each website has a MySQL database to store all the actual products, users, posts, appearance, etc. So we’ll install WordPress to make a nice, pretty user interface and act as a web builder, but the database is where all the actual information for your site is stored.


PHP is the language of your site. Tied in with Apache, PHP coding determines how plugins behave and interact with the user, and how to display them in a way that we can understand.


Webmin is a way to interact with your server through a web browser with a neat user interface, rather than solely through the command line. I’ve used Windows almost exclusively for most of my life, so Webmin is an easier way for me to interact with all the servers I operate. It also allows for easier file uploads directly to your site that are over the typical upload limit (usually 2MB, but we’ll talk about that later).

(Optional) phpMyAdmin

phpMyAdmin is another way (in addition to Webmin) of interacting with the database of your site. I prefer how phpMyAdmin presents the database versus Webmin, but note that to start messing with the database, you need to be careful and know what you’re doing.

Installation: Install Linux

Download the ISO Image

First up we need to download the Linux OS. I use Linux Mint, but for this guide you can also use Debian if desired. Either OS works just fine. For Linux Mint, I’m using the Xfce Edition since it is a very lightweight OS with minimal fluff. It does have a basic desktop that we’ll use briefly to finish setup later.

Head over to the Mint downloads page for Xfce (https://www.linuxmint.com/edition.php?id=307).

Pay attention to the verification notice; you’ll want to verify the ISO image you have downloaded is authentic. Scroll down and choose a mirror; I typically choose Harvard to download from, but any recognizable institution is fine as long as you verify the image. Follow the tutorial on how to complete verification.

Create the Bootable Media

Once you’ve grabbed the ISO image you’ll need to create a bootable drive using the USB thumb drive. The easiest way to do this on Windows is to download Balena Etcher. Once installed, you’ll hit “Flash from file”, select the ISO image you just downloaded, select the USB thumb drive, and then hit “Flash!”. Etcher will let you know when it’s done.


Boot into BIOS

Ethernet Access
While not required at this point, it is a good idea to have your device hooked up to wired internet at this point. Linux will prompt for updates to the repositories during installation, and doing it up front saves a step.

Plug the thumb drive into an open USB port on the mini-PC. You’ll be booting into the BIOS and changing your boot order first. Importantly, you don’t need to boot into Windows unless you just want to.

BIOS Access
Accessing the BIOS is specific to the PC you are using. If you have documentation that came with your PC, it will tell you how to boot into the BIOS. In general, you’ll boot into the BIOS right after the initial loading screen by pressing Escape, Delete, or a Function key. The correct key will typically be shown at the bottom of the boot screen briefly at startup; repeatedly pressing it won’t hurt anything. Missed it? Just reboot and try again.

Each BIOS is a little different; what you will need to do is find the boot order screen that tells your PC which device to load first. Because we want the PC to load and boot from the USB drive, we need that drive to be the top of the boot order. Once you’ve selected the USB drive as the primary boot device, save and exit the BIOS menu to reboot.

Install Linux

On the next boot cycle you’ll be brought into either the grub menu (a DOS-like basic menu), or the “prettified” Linux menu. In either case, press Enter to boot into Linux and start a live session.

Note that you have not actually installed Linux yet; this is a live session running directly from the USB stick. To install, you’ll need to double-click the “Install Linux Mint” icon on the desktop. Follow the prompts to install Linux, installing packages as you go.

When you get to the stage where the installer asks you for the type of installation, you want to “Erase disk and install Linux Mint”. This completely erases the hard drive, removing Windows, and installs Linux Mint as the only OS.

You can choose to encrypt the hard drive, but because we’re doing a headless install, I don’t recommend it. If the power goes out, you’ll need to be present to re-enter the encryption password before the server will boot, meaning your site may be down until you can do so. Make sure for your user you create a strong password, as this user will be able to access the root file system.

Once the installation is complete, you’ll be prompted to remove the USB stick and reboot.

Enable SSH

The next step, if you will be using a headless server, is to enable SSH. If you are not using a headless server, skip this step. My server is in a cabinet downstairs (with a handful of other servers); SSH enables me to remotely manage and access all my servers. To enable SSH, start by making sure everything is updated. From the desktop, click the menu button and then open the Terminal. Enter the following command:

sudo apt update && sudo apt upgrade -y
Code Breakdown
Lets look at what this does. “Sudo” means the code entered will be run as a super user (i.e., all privileges and permissions). “apt” means “Advanced Package Tool”, so we’re getting ready to use the package repository included in Linux. “Update” checks for any package updates from the published repositories, while “Upgrade” actually performs the upgrades. “&&” is a way to tag multiple commands together, and “-y” tells the system to go ahead and install the upgrades (without “-y” you’ll get a prompt from the system to confirm the upgrade).
Double Check Your Entries
In Linux, spelling and capitalization count! If it’s not capitalized, don’t get fancy! The command won’t work. Likewise, if you misspell a command, it won’t work.

Next, enable SSH:

sudo apt install openssh-server

Finally, you can check to see that the server installed successfully and is running:

ssh -V

Install and enable UFW

UFW stands for “Uncomplicated Firewall”. And its just that: a simple firewall that only does what you tell it. This machine will be open to the web, so we don’t need people messing around with it and hopping onto your network. Besides some changes to make to your primary router, we’ll use UFW to make sure only traffic we want to hit the server is successful, and only on selected ports.

So…what are ports?
Ports are specific “entryways” that internet traffic use. For example, traffic for websites will usually use ports 80 (HTTP) and/or 443 (HTTPS). Email, as another example, usually uses port 25, and SSH uses port 22. Ports are a way of determining what traffic is hitting the server, and what to do with it. I say “usually” because we can, to some extent, change default ports if we have multiple servers on the same machine listening to those ports (i.e. if Mastodon and WordPress are on the same server, they both listen on ports 80 and 443, so we would want to change one of those programs to listen on different ports to make sure traffic is routed correctly).

If you’re plugging along and haven’t taken a break, you don’t need to update and upgrade again. Install UFW:

sudo apt install ufw

Now, you’ll need to allow web service and SSH ports (if you are not doing a headless install, skip the SSH ports but do add the web ports). I strongly recommend that the SSH port is open only to the machine that you will be using to manage the server remotely from the local network. I use one PC in the house to do all my network management. If you have not already assigned static IP addresses, start with this article to do so and then head back here. To do this, first open up the SSH port. If you don’t want to limit it to one IP address it’s a simple command:

sudo ufw allow ssh

To add IP blocks, and to only allow from a specified IP:

sudo ufw allow from TARGET IP to DESTINATION port PORTNUMBER

Where the target IP is the calling IP address (your management PC), the destination is the virtual address (“any” if no virtual address), and the port you are granting access to. For example:

sudo ufw allow from to any port 22

Next, open up the web ports 80 and 443:

sudo ufw allow 80
sudo ufw allow 443

Finally, enable the firewall:

sudo ufw enable

You can check the status of the firewall by typing:

sudo ufw status
Headless Installs
If you are doing a headless install, stop here, shut down the machine, and move it to its permanent location and power it back on. If you will continue with a monitor, skip the next step.

Install PuTTY

To interact with the server over the network via SSH you’ll need an SSH client. PuTTY is one program that you can use on Windows. To get PuTTY, go to putty.org to download the SSH client. Once the server is relocated and powered on, you’ll open PuTTY from your networked PC and access it via its IP address over port 22:

Once opened, you’ll be brought directly to the terminal to enter your user (created during installation) and password:

Missing Something?
Heads up! Note that passwords do not display in the terminal! It’s there, just not visible.

Install Apache

Congratulations, you’ve made it to the next step. Let’s get started with Apache. Its already part of the package repository so this is an easy install. Start by updating and upgrading if you’ve taken a breather. Next, install apache:

sudo apt install apache2 -y

You can make sure the server is up and running by typing the server IP address into the browser bar of any PC on the local network and you’ll get the Apache landing page. Now, we’ve got to make sure Apache has permissions to make changes to the default web folder. First up, add your current user (we’ll use linux as the example user) to the default Apache group (www-data) and give ownership of those files to the Apache group:

sudo usermod -a -G www-data linux
sudo chown -R -f www-data:www-data /var/www/html

Logout for the changes to take effect:

The /var/www/ directory is where all of the files served by Apache will reside. This directory will host your website’s configuration files, plugins, and uploads.

Install PHP

Next up, install PHP. We’ll install all the required packages in one go:

sudo apt install php libapache2-mod-php php-mbstring php-mysql php-curl php-gd php-zip php-imagick -y

Install MariaDB (MySQL)

Lastly for the LAMP stack is the MySQL server. Start by installing the server:

sudo apt install mariadb-server

Next, secure the database:

sudo mysql_secure_installation

You will be prompted to create a password for the root user. In general, answer “Y” to all prompts to create a secure server. Additionally, create a strong, secure password and write it down. This password for the root user grants read/write access to the entire database, and can easily wipe out your site if used in error.

Now, we’re going to go ahead and set up the database for your WordPress site. Start by logging in to the database as the root user (using the super secure complex password you just created, that you can’t see while you type):

sudo mysql -u root -p

You’re now in MariaDB, so create your database (changing “mydatabase” to your database name of choice):


Now, create a user for this database. Note that we will be using this same user when we log in to WordPress for the first time to give WordPress access to the database. You’ll also create a password. This password should also be secure, but should not be the same as the root user password.

CREATE USER 'someuser'@'localhost' IDENTIFIED BY 'password';

Now, give the user you just created permissions for all tables in the database you just created:

GRANT ALL PRIVILEGES ON mydatabase.* TO 'someuser'@'localhost';

Now, flush the privilege table to apply changes:


And finally, quit the database:


Install WordPress

Congratulations, you’ve gotten this far. Now we’ll get the main program installed before we start digging into some of the nuances of the LAMP stack we’ll need to adjust. First up, lets get WordPress loaded and extracted. I like to keep my sites organized, so lets first create a folder for the site you’ll be creating:

sudo mkdir /var/www/mysite/public_html
sudo usermod -a -G www-data linux
sudo chown -R -f www-data:www-data /var/www/mysite

This created a new sub directory in the www directory and gave Apache permissions in that directory.

Now, move into the new directory you just made:

cd /var/www/mysite/public_html/

Download the WordPress tarball into the directory you just made and extract it:

sudo wget http://wordpress.org/latest.tar.gz
sudo tar xzf latest.tar.gz

Now, move the core WordPress files into the main directory:

sudo mv wordpress/* ./
WTF was that??
Yeah, Linux. So, “mv” is obviously “move”. The “/*” targets all things in the “wordpress” directory. When we downloaded WordPress, it downloaded as “wordpress.latest.tar.gz”, and extracted as “wordpress/”. “./” tells it to move all directories in the wordpress/ directory up one into the main directory.

Clean up your folder and get rid of the tarball:

sudo rm -rf wordpress latest.tar.gz

Make sure Apache has all the permissions needed for the files:

sudo usermod -a -G www-data linux
sudo chown -R -f www-data:www-data /var/www/mysite

Congrats, you’ve got WordPress installed. But…we’re not there yet.

Route Traffic to Your Site

Create DNS Records

You’ve got WordPress installed, but nothing points to it, yet. We’re getting ready to fix that. You also need an existing DNS record to obtain the required SSL certificate for your site. This step is actually very easy when you’re dealing with just a website. Adding email and external providers like SendGrid and MailChimp gets a little harder.

We’re going to create two “A” records. An “A” record is simply a record that tells the web that when I type mysite.com into a browser bar, is should resolve to IP address 123.456.789.1. This is your external IP address given by your ISP (Xfinity, Century Link, etc.). I’m going to go through the process on Cloudflare, but it is the same on pretty much each registrar.

First, go to the DNS zone of your registrar and then hit “Add Record”.

You’re going to create an A record. The name will be your site name (i.e., mysite.com), and it will point to your external IP address. If you’re not sure what that address is, contact your ISP or log into your home router; it will generally display your external IP address under the main page somewhere. Go ahead and leave everything else as is; you want traffic proxied through Cloudflare to keep your true IP address hidden from the world.

Now, we’re also going to add another “A” record. This one will add the “www”. You notice that you don’t usually have to type “www.mysite.com” anymore; that’s the beauty of progress. However, some people, by habit, will still type “www” in front of everything. We’ll need to add another “A” record to fix that. So repeat the process of adding a record again. However, this time for name just put “www” and then your external IP. What this does is say that “www.mysite.com” is an alias of “mysite.com” and points to IP address 123.456.789.1.

Bad time to take a break
At this point, any traffic for your site will point to your IP address. Right now, that will bump up on your router login page. I don’t recommend taking a break right now until you have completed at least the basic WordPress installation steps. While your site is new, bots will typically find it fast to start crawling and indexing for search engines.
DNS Propagation
As a rule of thumb it takes about 48 hours for new DNS records to fully propagate through the web and for your site to be reachable. That has not been the case with Cloudflare. As a large CDN, DNS propagation is, in my experience, essentially instantaneous because they operate so many distributed servers. If you’re using other registrars, DNS propagation may take longer.

Port Forward Your Router

Next step, essentially working from the outside-in, is to port forward your main router. You want all ports closed except for port 80 and port 443. This process is specific to each router; you will need to consult your router documentation for how to log in and create port forwards. You will need to forward both port 80 and port 443 to the internal IP address of your web server.

Security Note
If possible, you should create a separate internal network only for your web server that is isolated from all your other devices. Some routers call this a DMZ; I’ve got my web-facing devices on a separate VLAN that cannot connect to any other network. See this article for a more detailed explanation of VLANs, and check your router documentation for details on how to complete setup.

Create a Virtual Host in Apache

Next step is to create a record in Apache that tells it how to handle incoming requests for “mysite.com”. Since you only have one site, we’ll only create one host. That being said, you can host multiple websites on the same machine using multiple virtual hosts; Apache will match the incoming requests using the qualified domain name and point them to the correct directory. Essentially, you’re setting up a text file that says “any incoming request for mysite.com will be shown the site defined by files in /var/www/mysite.com/”.

Start by opening up PuTTY and SSHing into your web server. We’re going to create a site config, so start with (replacing mysite with your domain):

sudo nano /etc/apache2/sites-available/mysite.conf

This creates a new text file in available sites called mysite. We need to add some directives to this config file:

<VirtualHost *:80>
        ServerName mysite.com
        ServerAlias www.mysite.com
        DocumentRoot /var/www/mysite.com/public_html
        ErrorLog ${APACHE_LOG_DIR}/mysite.com_error.log
        CustomLog ${APACHE_LOG_DIR}/mysite.com_access.log combined

Hit “Ctrl+X”, then “Y” to save and exit out of the editor. You’ve told Apache how to handle incoming, HTTP (unsecured) requests on port 80 and where to log errors, but its not enabled yet. To enable the site, type:

sudo a2ensite mysite.conf

Lastly, we need to tell Apache to reload its config files to recognize the new site:

sudo systemctl reload apache2

We’re almost there. The next step is to enable HTTPS for your site. Of note, you could do this later, but my experience has been that it’s easier to complete WordPress setup if HTTPS is already enabled.

Install Certbot and enable HTTPS

We’ll use Certbot to automate SSL certificates. It will run the challenges and obtain certificates from Lets Encrypt and keep them updated in the background. First up, if not already done, update and upgrade your system. Next, install Certbot for Apache:

sudo apt install python3-certbot-apache

Now, grab the certificate and install it into Apache (which will create the necessary virtual host):

sudo certbot --apache

Your site should show as option 1. Type in the appropriate number and hit “Enter”. For first-time setup, you will need to provide an associated email address that Lets Encrypt can tie the certificate to. Follow the prompts to complete installation.

Note that you might need to bypass the proxy function of Cloudflare temporarily while grabbing the SSL certificate. I have not had this happen recently, as I believe Cloudflare can now recognize and route SSL requests appropriately. But, if you have an issue with obtaining the certificate, pause Cloudflare on the site. Make sure your router is port forwarding correctly, and lastly double-check your IP addresses.

Once complete, a new virtual host configuration file will be created, and the original config will be updated to include a rewrite directive that essentially tells Apache to route any HTTP requests to port 443 and convert them to HTTPS requests. Note that we will also set this up on Cloudflare, so in theory no incoming requests will ever be insecure.

Edit the Apache Config File

Finally, we’ll need to make sure Apache can modify and override changes in the /var/www directory. This is a small change, but without it your site may not work because Apache won’t be able to modify the .htaccess file. Open PuTTY and we’re going to edit the Apache config file:

sudo nano /etc/apache2/apache2.conf

Look for and modify the following block of text:


<Directory /var/www>
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted


<Directory /var/www>
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted

Exit out of the editor (“Ctrl+x” then “Y”) and reload Apache:

sudo systemctl reload apache2

Guess what? It’s finally time to start fiddling with WordPress.

Complete WordPress First-Time Setup

You’re there! If you’ve followed successfully, type your site address into the browser bar to be greeted by the WordPress 5-minute setup:

Follow the prompts until you get to the database screen. This screen requires input of your database credentials:

So, for this page, enter the following (remember when we created the MySQL database?):

  • Database name: the name you gave the database created during the MySQL section
  • Username: the user you created for that database (not “root”)
  • Password: the password you gave that user (remember: NOT the root password)
  • Database Host: localhost
  • Table Prefix: change this to a random 4-character string, doesn’t matter what it is as long as it is not wp_. Hackers looking for soft targets can access your site using the default wp_ prefix and can view all the data in those tables (including user information).

Hit submit, if correct WordPress will confirm database connection, and hit “Run the Installation”. Once complete, you’ll come to the final installation step:

Punch in your site title and pick a username and strong password. Note that this will be your admin user for the site going forward, so make it a strong password. Finally, enter a good email address. If you don’t want your site public to the world and want to hide it from search engines, check the box to discourage bots. Note that it may not be honored. Hit install, wait for it to finish, and you should land on the login page. Log in and you’ll hit the dashboard:

Navigating the Dashboard

Once on the dashboard, I’ll call your attention out to a few different items. First up, lets make sure your site is running smoothly and we didn’t miss any PHP modules needed for functionality. PHP modules define how your site handles certain actions, like photo uploads. Go ahead and click the link on the dash to visit the Site Health screen.

From here you’ll get information on your site’s behavior, as well as any issues that need to be addressed. For example; this test site has an issue with the REST API. While not critical, the REST API is important for publishing posts and pages. That being said, this is a test site that I spun up quickly for illustration, and the REST API is not going to behave correctly. Likewise, the remainder of the improvements are issues you should address quickly, but won’t break your site.

This site was missing several PHP modules, but I’ve already fixed those. Normally, those would show up as critical improvements and opening the issue tells you exactly which components are missing. In this case, PHP was missing the -gd, -curl, -imagick, and -zip modules. To fix that, you’ll need to download those modules. In PuTTY, grab the modules and then reload Apache:

sudo apt install php-curl php-gd php-imagick php-zip -y
sudo systemctl reload apache2

If you still seem to be missing those modules, you’ll need to make sure you’re getting the right PHP modules for your PHP version. Check your version by going back to the Site Health screen and opening the “info” tab. Scroll down to the Server drop-down:

Take note of the PHP version (8.1.21 in our example). Now, in PuTTY, try again with the PHP version for your site with just the root version:

sudo apt install php8.1-gd php8.1-curl php8.1-imagick php8.1-zip -y
sudo systemctl reload apache2

Once you’ve at least gotten the PHP modules taken care of, hover your mouse over the home icon in the upper left corner and hit “Visit Site”.

So it’s a little boring right now, and this is what the world sees. But you’ve got a blank slate. Literally, you can do whatever you want from this point. However, lets hit a few remaining items. Go back to the dashboard by hitting the gauge icon in the upper left, on the admin ribbon.


The first thing you should do, before you make any new pages, is to decide what you want your link structure to look like. Your permalink structure determines how your URL structure is created (everything after the base URL of https://mysite.com/). In other words, if you write a post titled “Website 101”, the plain WordPress permalink structure will assign an address that uses the post ID, not the title. However, changing that can let you have the URL structured to include the post name, and it helps with search engine results (if you want your site ranked by search engines).

Permalink Changes
Changing your permalink structure after you’ve started adding pages and posts is usually a very bad idea. Doing so will almost always break your site, as the server will be looking for pages that no longer exist. You can do it, but generally it requires URL rewrites and changes to the .htaccess file, and it’s much easier to just settle on a permalink structure now rather than later.

To change your permalink structure, on the left menu tree open “Settings→Permalinks”.

Select the structure you would like, and WordPress details what that structure will look like. In general, I like a simple “Post Name” structure (https://mysite.com/post-name/). Some plugins will require this permalink structure, but ultimately, it’s up to you. If you are a heavy blogger, you may want your site organized by month and name or day and name. Your site, your choice.

Public or not Public

I won’t dig too much into the WordPress settings, but head over to the “Settings→General” menu. Under this page, note the “Membership” option. Checking this box allows anyone to register for your site. Right now, only users you create can log in. This option opens the site up for everyone. We’ll touch a little more on the implications of this with WordPress in a bit when we’re discussing recommended plugins.

So Many Plugins

Next, head over to the Plugin menu.

Plugins are where it’s at with WordPress. They determine your site functionality, enhance your site, and add tons of features. Because you are self-hosting your site, you can add unlimited plugins. The paid WordPress.org hosting requires a paid plan to add plugins, and there are limits. Note that more plugins is not always better, and each site is different.

To add a new plugin, simply hit the “Add New” button. You’ll get a list of recommended plugins, and you can search keywords for specific plugins.

When choosing plugins, there are a few things you should watch out for:

  • Check out the plugin rating. A poorly rated plugin is likely buggy and not well maintained.
  • Look at the last update. A plugin updated over a year ago is likely no longer maintained, or not maintained very well and may open up a security risk
  • How many people are using it? A plugin with 5 users updated 3 days ago may have a 5-star rating, but given the choice between a 3.5-4 star plugin with 5+ million users to accomplish the same goal, I’ll go with the plugin that has more users. That doesn’t mean it’s a bad plugin, but you should be cautious.

We’ll hit on several plugins that I recommend for site functionality, and to help improve site performance and security.

Recommended Additions

Let’s look at a few things I recommend personally, for any site. Most all of these are something you’ll do with a plugin, but some are settings you’ll need to adjust if you use Cloudflare.

Adjust Cloudflare Encryption

First up make sure your Cloudflare Encryption settings are set to Full (Strict). To do so, login to your Cloudflare dashboard and select your domain. On the left menu tree, click on SSL/TLS. Make sure the SSL/TLS encryption mode is set to Full.

Essentially, this encrypts traffic end-to-end; because we grabbed a certificate through Certbot, you’ve already got a trusted certificate.

Block plugin requests

Also in Cloudflare, we want to block requests to access your plugin folder that do not originate from your site. This is a firewall rule that we’ll add. In general, unless you change the folder name that plugins will reside in (recommended), we want to block requests to the default plugin folder originating from outside your server. These will usually be probes for weaknesses or exploits. To do so, login to Cloudflare, click on your domain, open the Security drop-down menu, and click WAF

We’re going to add a rule (the free plan allows up to 5). I’d just call the rule “Plugin”. We’re adding two fields. So, if incoming requests match:

  • The Field is “URL Path”, operator “contains”, value “/wp-content/plugins”


  • The Field is “Referer”, operator “does not contain”, value “themays.me”

The action is to “Block”.

More firewall rules should be added, but this a basic, easy rule to add.

Set up Redis

Redis is object-caching software that we’ll install to help improve site performance. Recent WordPress versions actually look for object caching, and its an easy step. In PuTTY, make sure your system is updated and type:

sudo apt install redis-server

Now run the Redis server:

sudo systemctl enable --now redis

Finally, make sure the server is running as a system service. Type:

sudo nano /etc/redis/redis.conf

Find the line that says “supervised no” and change no to systemd.

Left ImageRight Image

Exit the editor, saving changes.

Next, we’ll need a plugin to utilize the Redis server. Go to plugins and Add New. Search for “Redis Object Cache”. Install and activate the plugin, then follow the prompts to enable the connection.

Set up a user management system

This is a little more involved, but if you decide that you want to allow open registration to your site and if you are allowing payments on your site, you should pick a decent user management plugin that provides a login page other than the standard WordPress login page. First, this looks more professional and can stay true to your brand. Second, it’s considered best practice from a security standpoint to hide the standard WordPress wp-admin and wp-login pages.

As an example, I use UsersWP to create and manage my login, profile, and account pages.

There are many different user management options. I use the paid version, and with that comes site messaging, followers, groups, online status, enhanced email notifications, and other fun integrations. Your choice, but expanding beyond the integrated WordPress user management is a must in my book.

Create a site Backup

Accidents happen. You want to be able to recover your site if you wipe it. Just FYI…I’ve managed to do that. Fortunately…there’s a plugin for that. I recommend UpdraftPlus Backups.

With UpdraftPlus you can back your entire site up to remote storage. You don’t want to backup to a local hard drive, since if the server fails and the hard drive fails with it, you need to be able to pull your site from another location. You can set up backups to Google, OneDrive, FTP, and others with the free version.

Create a Privacy Policy and Terms of Service

The last thing I’ll point out is to create a Privacy Policy page and a Terms of Service page. The reason for both is in the case of both integrations and legal CYA. For example, I have an integration with Facebook to allow social logins. However, for the integration to work I have to prove to Facebook I have a published Privacy Policy page. WordPress actually has suggestions on how to create this page, and it’s fairly easy enough to do.

As for a Terms of Service page, you can research some options on the web. Various agencies exist that will help create a Terms of Service page. Again, this is a CYA more than anything else.

Wrap Up

And that, is that. Start to finish, you have set up a functional WordPress site. Start exploring the many, many ways you can design and personalize your corner of the web, and make it yours.

Author: Zack

Pharmacist, tech guy, pianist, lover of beer, gamer, beach bum. Probably missed something. Just assume I'm into a little bit of everything.


No responses yet

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
Filter by Categories
Alcoholic Drinks (Cocktails)
Cool Stuff
Fish and Seafood
Home and Family
Home Automation
Home Security
Orlando, FL
Recipe of the Week
Web Hosting