Guide: Manual WordPress Installation

In this complete guide, I'm going to show you how to install WordPress manually and why it is a good decision. We are going to setup and secure our Linux server, install the latest version of MySQL and PHP as well as install and configure Nginx. You will learn how to secure your server with a firewall and encrypt communication between the users and the website with a TLS certificate. At least, Cloudflare will be used for caching our content.

author: Besjan Sejrani

Besjan Sejrani

WordPress logo on a hero image explaining how to manually install WordPress

What is WordPress ?

WordPress is a content management system, also called CMS, which is mostly used for blogging, ecommerce and website presentation. It is Open Source, you can use it at any cost for personal or commercial use. These days in 2022, WordPress counts for about 40% of the entire web. Since it is such a large project, you can easily find help via the documentation or tutorials on the web.


Why manual WordPress installation is better ?

Installing WordPress manually compared to other WordPress hosting solutions isn't necessary a better choice. However, it will teaches you valuable information about the web, which you can use later on for other websites. You will never depend or be locked in by some WordPress hosting solutions, you will have total control.


Prerequisites

For this guide, you don't need to have any computer skills, you will learn the basics. However, it is recommended to have a strong will to learn new subjects and practice a long.

If you want to host your website, you will need to have a hosting provider, such as Vultr or Digital Ocean, and a domain name. You will have to pay $5-10 / month for hosting and $10 for a domain / year.


Hosting Provider

For this complete guide, Vultr will be used for hosting. The domain name selfticode.com has been bought and wit ill be managed via Infomaniak's DNS configuration panel.

You can use any hosting and DNS solution you want, as long as you can manage your server via SSH. Be careful, often shared hosting doesn't allow for SSH management.

Image: Create an instance on Vultr

Image: Choose your Vultr server, server location, server OS and server size

Image: Choose your Vultr server, server location, server OS and server size

In this guide, Ubuntu was chosen as the main operating system, but you can choose any Unix like OS you want, therefore, package manager commands may be differ.


SSH Configuration

Secure Shell, also known as SSH, is a network protocol which allows to connect to a server securely. The encryption algorithm generates a private key, which must stay private and a public key, which you can share with the server.

Generate a SSH key

Windows users must install Git Bash before executing the ssh-keygen command. For Mac and Linux users, it can be achieved by opening the Terminal. Enter the command and provide a valid email address.

By default, the ssh-keygen command will create two files, one file called id_ed25519 and an other file named id_ed25519.pub. If you want to change the name of the files and their locations, you can do it by editing the path file. However, it is strongly recommended to create the files under the hidden folder .ssh.

# Creates a private and public key
ssh-keygen -t ed25519 -C "[email protected]"
# Copy the id_ed25519.pub content
cat ~/.ssh/id_ed25519.pub

It is recommended to add your public SSH key to Vultr, this will make the connection process to the server much more easier and secure.

To show the content of the public SSH key, the cat command is used, then a simple copy and paste does the trick. Make sure to copy the file ending in .pub.

Image: Add your public SSH key to Vultr

# SSH connection
ssh [email protected]_address

Create & secure new super user account

By default, the root account is the super user account and this is a problem since everyone knows that a root account exists.

The best practices are to create a new user and to add it to the sudo group which will grand super user permissions, disable SSH connection for the root account, and finally disable password authentication.

# Creates a new user and asks for new password
adduser username
# Add user to sudo group for super user permissions
sudo usermod -aG sudo username
# Verify that the user as been added to the sudo group
groups username
# Switch to user account
su - username
# Exit new super user account
exit
# Exit server
exit
# Send public key over SSH on the new super user account
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]_address
# SSH connection with the new super user account
ssh [email protected]_address

The ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]_address command will send the public SSH key on the new super user account. It's the same process as Vultr did when we pass to it the SSH public key.

# Edit the SSH serveur configuration file
sudo nano /etc/ssh/sshd_config
# Modify the following properties
# If a # stands before the property, the line is commented out
PermitRootLogin: no
PubkeyAuthentication: yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication: no
# Restart SSH server
sudo systemctl restart sshd

You can now try to connect via SSH to the root account, it will not work. It will only work with the new super user created.


PHP Installation

WordPress uses PHP as the main programming language on the server. To install it, we first need to update all the packages on our server, then install PHP and some extensions which will add some further capabilities to PHP.

# Add package repository, Ondrej PPA maintains a repository that includes multiple PHP versions
sudo add-apt-repository ppa:ondrej/php
# Download packages information
sudo apt update
# Upgrade packages
sudo apt upgrade

The sudo command stands for super user do, it will allow you to execute some commands in privilege mode.

# Install PHP and PHP extensions
sudo apt install php8.0-fpm php8.0-common php8.0-mysql \
php8.0-xml php8.0-xmlrpc php8.0-curl php8.0-gd \
php8.0-imagick php8.0-cli php8.0-dev php8.0-imap \
php8.0-mbstring php8.0-opcache php8.0-redis \
php8.0-soap php8.0-zip -y

The following extensions will enhance PHP's capabilities to support the MySQL database, XML format, image processing, mail support, zip compression and Redis database support.

# Verify your PHP version
php --version
# PHP 8.0.14 (cli) (built: Dec 20 2021 21:22:57) ( NTS )
# Copyright (c) The PHP Group
# Zend Engine v4.0.14, Copyright (c) Zend Technologies
# with Zend OPcache v8.0.14, Copyright (c), by Zend Technologies

After the installation has been completed, verify the PHP version.


MySQL Installation

MySQL is a SQL relational database which is used for storing data. WordPress uses MySQL for it's internal functioning, especially in the case of themes and plugins.

You could also use MariaDB, which is a clone of MySQL. Side note, it was cloned of fear that Oracle, when it acquired the project, would transform it into a payed product.

# Download packages information
sudo apt update
# Install MySQL
sudo apt install mysql-server

After updating your packages, install MySQL.

# Configure MySQL
sudo mysql_secure_installation

It is recommended to add a password to the MySQL root user, preferably, a strong password. Then remove anonymous users, disallow root login remotely and finally remove the test database.

# Connecting to MySQL as super user
sudo mysql
# Create WordPress database
CREATE DATABASE wordpress;
# Create MySQL user
CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
# Grant permissions to user
GRANT CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, SELECT, REFERENCES, RELOAD on *.* TO 'username'@'localhost' WITH GRANT OPTION;
# Frees any memory that the server has cached
FLUSH PRIVILEGES;
# Exit MySQL shell
EXIT;

After creating the wordpress database and creating a user identified by a password, the permissions of that user are enhanced so that it can perform basic database operations. Finally, the database memory is reset for security reasons before exiting the database.


Download WordPress

WordPress source code can be downloaded on the wordpress.org website via the wget command.

# Go to root user directory
cd ~
# Install WordPress tar files
# wget downloads WordPress via the wordpress.org repository
wget http://wordpress.org/latest.tar.gz
# Extract files
# This will create a folder named wordpress containing all the WordPress files via extraction
tar -xzvf latest.tar.gz

After WordPress is downloaded from the wordpress.org repository, the content still needs to be extracted into a folder named wordpress.

# Go to wordpress directory
cd wordpress
# Copy wp-config-sample.php file
cp wp-config-sample.php wp-config.php
# Editing wp-config.php with nano editor
nano wp-config.php

Nano is a text editor for Linux, you can use it for modifying your files. For beginners it is recommended to first start out with nano before using other text editors such as Vim or Emacs.

#/** The name of the database for WordPress */
define( 'DB_NAME', 'database_name_here' );
#/** MySQL database username */
define( 'DB_USER', 'username_here' );
#/** MySQL database password */
define( 'DB_PASSWORD', 'password_here' );
# Go to the following URL https://api.wordpress.org/secret-key/1.1/salt/
# Copy and paste the following content
define('AUTH_KEY', 'YOUR AUTH_KEY');
define('SECURE_AUTH_KEY', 'YOUR SECURE_AUTH_KEY');
define('LOGGED_IN_KEY', 'YOUR LOGGED_IN_KEY');
define('NONCE_KEY', 'YOUR NONCE_KEY');
define('AUTH_SALT', 'YOUR AUTH_SALT');
define('SECURE_AUTH_SALT', 'YOUR SECURE_AUTH_SALT');
define('LOGGED_IN_SALT', 'YOUR LOGGED_IN_SALT');
define('NONCE_SALT', 'YOUR NONCE_SALT');

After opening the wp-config.php file with your text editor, you must modify the database_name_here, username_here and password_here variables.

For security reasons, you should also modify the security variables. Via the following URL, https://api.wordpress.org/secret-key/1.1/salt/, WordPress generates automatically, random values for the security variables. Just copy the content and replace it on the file.


Nginx Installation & Setup

Nginx is a web server sitting just in front of a server, it is capable of reverse proxying and load balancing, which means it redirects traffic, depending on the URL that the user has entered, to a single server or to multiple servers.

Nginx reverse proxy diagram

Image: Request & response flow

Compared to other web server such as Apache, Nginx handles requests much more efficient, resulting in a greater capability of handling more requests.

# Download packages
sudo apt update
# Install Nginx
sudo apt install nginx -y
# Modify the nginx configuration file
sudo nano /etc/nginx/sites-available/default

After updating you packages, install Nginx and update the default configuration file under /etc/nginx/sites-available/default with the following content. Modify your server_name with your domain name, use the nano editor to modify the file.

server {
listen 80;
server_name selfticode.com;
root /var/www/html/wordpress;
index index.php;
# For security reasons the server_tokens is set to off
# The browser will not be able to know which OS is the server
server_tokens off;
location / {
# The requested URI is first tested, then the folder, and finally the index.php file with any query string
try_files $uri $uri/ /index.php?$args;
}
location ~* /wp-sitemap.*\.xml {
# The requested URI is first tested, then the folder, and finally the index.php file with any query string
try_files $uri $uri/ /index.php$is_args$args;
}
client_max_body_size 100M;
# Pass the php scripts to FastCGI server specified in upstream declaration.
location ~ \.php(/|$) {
include fastcgi.conf;
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
try_files $uri $uri/ /app.php$is_args$args;
fastcgi_intercept_errors on;
}
# GZIP is a compression algorithm, it reduces the files payload
gzip on;
# The higher the gzip_comp_level is, the better it will compress files
# The downside is that it's CPU load heavy
gzip_comp_level 4;
gzip_min_length 1000;
gzip_proxied any;
# File types allowed for Gzip compression
gzip_types
application/geo+json
application/javascript
application/x-javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xml
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
# Files, Media
# A large selection of file formats was selected for any use case
location ~* \.(?:css?|js?|jpe?g|png|gif|ico|webp|tiff?|mp3|m4a|aac|ogg|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
expires 90d;
access_log off;
}
# Static assets
location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
add_header Access-Control-Allow-Origin "*";
expires 90d;
access_log off;
}
}
# Verify Nginx configuration file
sudo nginx -t
# Restart Nginx
sudo systemctl restart nginx

Verify that the nginx configuration file is correct, and only then restart nginx.

# Moves the wordpress folder to a new location
sudo mv ~/wordpress /var/www/html/wordpress
# Changes the the owner of the folder and it's content recursively
sudo chown -R www-data:www-data /var/www/html/wordpress/
# Changes the the permissions of the folder and it's content recursively
sudo chmod -R 755 /var/www/html/wordpress/

By default, nginx runs with the www-data user, for this reason, we need to change the owner of the wordpress folder and change the folder's permissions.

The chown -R command changes the owner recursively, that means that the folder and all the content inside it will change owner to www-data.

The chmod -R command changes the folder's permissions. Folder permissions are arranged in user:group:other format. Each one of them gets some permissions. The number for the reading permission is 4, writing permission is 2 and for the execution permission is 1. Combined, the user can read, write and execute. The group can read and execute and finally the others can read and execute as well.

When you enter your IP address on the URL, you should see the WordPress installation wizard, but don't run it yet, we haven't finished installing and securing your website.

Firewall diagram

Image: WordPress Installation isn't completed yet

Firewall Issue

If you can't see the WordPress installation page, it may be the fault of the ufw service, ufw stands for uncomplicated firewall. We will stop and disable the ufw service in favor of the nftables firewall, which is a more granular firewall solution, we will install it at the end.

# List all services running on the server
service --status-all
# Restart Nginx
sudo systemctl disable ufw
sudo systemctl stop ufw

DNS Binding

When you purchase a hosting plan, you get your server and a public IP address which you can then link to a domain name. A domain name helps users remember your website via a human-readable URL. Otherwise, they would have to remember four-byte IP addresses, which is neither convenient nor useful when searching for information.

You can link your domain name to your server through any domain name provider, as long as you have direct access to your DNS control panel. After installing the server you need to change the DNS configuration settings, just add ns1.vultr.com and ns2.vultr.com to your custom DNS server settings, after a while it should work.

A DNS A record allows you to link a domain name to a IP address. A DNS CNAME record allows you to create an alias when you want redirect your trafic from www.selfticode.com to selfticode.com

DNS control panel

Image: Infomaniak's DNS control panel

Type Hostname Value
A selfticode.com 134.122.90.217
CNAME www.selfticode.com selfticode.com

TLS certificate

Transport Layer Security, also known as TLS, is a security protocol used to encrypt the user's connection to the website they are visiting. With TLS, it is not possible to perform a "man in the middle" attack, which means that a malicious user impersonates the web browser from the user's point of view and he also impersonates the user from the browser's point of view with the ultimate goal of stealing security credentials.

The only thing that the malicious user will see is encrypted data.

Why are TLS certificates important ?

Security plays a major role in the modern Internet, especially with regard to search engine rankings and user trust. If you process sensitive information via a form or sell products on your website, users expect secure communication between the browser and the website.

In the recent years, Google has enhanced it's search ranking factors, and one of them is security. Google will rank your website higher in search engines result if your website implements a TLS certificate.

Let's Encrypt

Let's Encrypt is a non profit certificate authority which provides TLS certificates. Before Let's Encrypt, you would have to pay for implementing a TLS certificate from a certificate authority, these days, security is more then ever encouraged to implement.

Certbot

Certbot is a free and open source automation tool which will allow you to implement a TLS certificate on your web server for free, it will automatically generate a certificate and modify your nginx configuration file.

# Install snapd
# Verify latest version of snapd
sudo snap install core; sudo snap refresh core
# Install Certbot
sudo snap install --classic certbot
# Create a symlink
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# Run Certbot on Nginx web server
sudo certbot --nginx

By running Certbot, it will add a new server section in the nginx configuration file listening on port 443 for HTTPS requests.

Certificate Renewal

By default, Let's Encrypt TLS certificates are only valid for a time period of 90 days. So you have to run the process over and over again without forgeting it. Fortunately, we can automate the process without doing any manual action, this can be done with the help of cron jobs.

Cron jobs are repetitive actions that can be executed by a script during a time interval. These intervals can be configured to fire multiple times during this period.

# Cron configuration file
crontab -e
# Certbot will try to run the renew process daily
@daily certbot renew -n -q

Firewall

A firewall is like a security guard, it controls the traffic which wants to come in and out. One important measure for securing a server would be to implement a firewall for filtering unwanted requests. Nftables will be used as a popular firewall solution.

# Download packages
sudo apt update
# Install ntables
sudo apt install nftables -y
# Modify the nftables configuration file
sudo nano /etc/nftables.conf

After nftables is installed, the configuration file must still be edited and the service needs to be restarted.

#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state invalid drop comment "Drop invalid connections"
ct state established,related accept comment "Accept traffic originated from us"
icmp type echo-request drop
icmp type echo-request limit rate 10/second accept
tcp dport {80, 443} accept
tcp dport 22 accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0;
}
}
# Restart nftables
sudo systemctl restart nftables

After configuring nftables correctly, restart the service and open a new terminal. Test your SSH connectivity as well as all the other rules you allowed in the configuration file. In the worse case scenario, you will be locked out of your server but you will still be able to modify the configuration file on the opened terminal tab.

Firewall diagram

Image: The firewall controls the in going and outgoing traffic


Cloudflare caching

Cloudflare is a CDN, also known as a Content Delivery Network, it acts like a reverse proxy and sits in front of a web server. The advantage of using Cloudflare is that they have servers all around the world so that they can cache our website's static content and protect us against DDOS attacks.

Caching is the process of storing the temporary content of a request, so that when a second request asks for the same resources, a faster response time can be delivered. This is really great if you main server is in Europe, but you have users all around the world. The CDN will act as a middleman, which will shorten the distance that the request will have to make.

Distributed Denial of Service, also known as DDoS, is the act of attacking a server with hundreds of thousand of requests so that it will be unable to respond to all of them. This attack as of purpose to harm the website's traffic and bring them down temporary.

Image: Cloudflare caching & DDoS protection diagram


Conclusion

I hope your learned something in this article and that it helped you to install WordPress as well as secure your server. Now you know the pros and cons of a manual WordPress installation, if you choose the manual way, you will have total control of your website.

WordPress Installation completed

Image: Congratulation you have installed WordPress

We use cookies to provide our services, for analytics and marketing. To find out more about our use of cookies, please see our Privacy Policy. By continuing to browse our website, you agree to our use of cookies.