Step-by-Step Guide: Setting Up WordPress with Nginx on Ubuntu 24.04 (LTS)
1. Create and Secure a Virtual Private Server
(Current Step) 2. Install Nginx PHP 8.3 WP-CLI MySQL
3. Setting Up WordPress with SSL on Nginx
- Set Up Domain on Cloudflare (Skip this if you want use Cloudflare in Step 4)
- Get SSL with Certbot (Skip this if you want use Cloudflare in Step 4)
- Configure Nginx to Host Website
- Create Database for WordPress
- Install WordPress with WP-CLI
- Add More Site to Your Server
4. Set Up Free SSL/TLS with Cloudflare to Nginx (Skip this if you using Certbot in Step 3)
5. Set Up Redis Object Cache and Nginx FastCGI Page Cache
Building Your WordPress Server: Nginx, PHP, and MySQL
Chapter 1: Create and Secured your DigitalOcean VPS with Ubuntu 24.04. Now, let’s install the essential components:
- Nginx (pronounced Engine-X): A powerful and efficient web server to deliver your website content.
- PHP-FPM (FastCGI Process Manager): Processes PHP code dynamically to create web pages.
- MySQL: A robust database to store your website’s content and user data.
- WP-CLI: lets you skip the web browser and manage your WordPress site with text commands.
Together, these form the LEMP stack, the foundation for your WordPress server.
To proceed, ensure you have an active SSH connection to your server.
ssh newuser@tutorial.caklus.com
caklus@192 ~ % ssh newuser@tutorial.caklus.com
Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-36-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Tue Jul 9 15:16:42 WIB 2024
System load: 0.0 Processes: 99
Usage of /: 3.6% of 47.39GB Users logged in: 0
Memory usage: 11% IPv4 address for eth0: 167.172.77.192
Swap usage: 0% IPv4 address for eth0: 10.15.0.5
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Last login: Tue Jul 9 06:30:39 2024 from 125.165.103.249
newuser@tutorial:~$
1. Installing Engine-X Web Server
Engine-X is a popular web server software widely used on Linux servers.While Ubuntu offers packages, they might not be the most recent version. To ensure optimal performance and security, we’ll install from the official repository maintained by Ondřej Surý, known for providing the latest stable packages.
- First, add the Engine-X repository and update the package list to reflect available software.
sudo add-apt-repository ppa:ondrej/nginx -y
sudo apt update
- The update may reveal available upgrades for other packages. Let’s install them for optimal security and performance:
sudo apt dist-upgrade -y
- With the repository added and packages updated, install Engine-X using the following command:
sudo apt install nginx -y
- Once the installation is complete, verify if installed successfully with the following command:
nginx -v
newuser@tutorial:~$ nginx -v
nginx version: nginx/1.26.1
Once the installation is complete, open a web browser and navigate to http://your_server_ip_address (replace with your actual IP) or hostname we set up before http://tutorial.caklus.com (replace with your hostname). You should see the default welcome page.
Important Note: Browsers now default to secure connections (HTTPS). Since we haven’t set up SSL yet, using http:// is necessary for this test. We’ll address SSL certificates in a later step to ensure secure communication.
While Engine-X is well-optimized by default, let’s make some adjustments. To ensure efficient configuration, we’ll first check two server settings:
- CPU Core Count: This helps determine how Engine-X handles workloads.
- Open File Limit: This defines the number of files Engine-X can access simultaneously.
We’ll then proceed with editing the configuration file.
- This command displays the number of CPU cores available on your server. Remember this number for later configuration.
grep processor /proc/cpuinfo | wc -l
- Next, use this command to see your server’s current open file limit (remember the value):
ulimit -n
- Let’s edit the main configuration file:
sudo nano /etc/nginx/nginx.conf
- By default, change the user directive in the configuration file to your current username. This simplifies permission management, but be aware this approach is only acceptable for single-user development servers. For production environments with multiple users, using a dedicated system user for Engine-X is a more secure practice.
- Set the worker_processes directive to match your server’s CPU core count (previously identified). This optimizes Engine-X workload handling.
- Set the worker_connections directive in the events block to your server’s open file limit (previously checked with
ulimit -n
). This defines how many connections each worker process can handle. - Uncomment the multi_accept on directive in the events block. This allows each worker process to accept multiple new connections simultaneously, improving efficiency.
- Add the keepalive_timeout directive (just above sendfile on;) and set it to a lower value (e.g., 15). This reduces the time idle connections stay open, freeing them for new clients.
- Uncomment the server_tokens directive in the and set it to off. This prevents Engine-X from revealing its version number in error messages and response headers, reducing potential security vulnerabilities by making it harder for attackers to identify the specific software you’re using.
- Add a line to define the maximum upload size for the WordPress Media Library using the client_max_body_size directive. I choose a value of 64m but you can increase it if you run into issues uploading large files.
- Uncomment gzip_proxied and set it to any to compress all responses sent through a proxy server.
- Uncomment gzip_comp_level and set it to a moderate value (e.g., 4 or 5). This improves compression without significantly impacting CPU usage.
- Keep the default values for the gzip_types directive. This ensures efficient compression of JavaScript, CSS, and other common static files along with HTML.
user newuser;
worker_processes 1;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
multi_accept on;
}
http {
##
# Basic Settings
##
keepalive_timeout 15;
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
server_tokens off;
client_max_body_size 64m;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml applicat>
- Once you’ve made your edits, press Ctrl+X and then Y to confirm saving the configuration file.
- Before restarting, it’s crucial to check for errors in the configuration. Run the following command to verify:
sudo nginx -t
newuser@tutorial:~$ sudo nginx -t
[sudo] password for newuser:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
- If the syntax is correct, proceed with restarting using:
sudo service nginx restart
- If isn’t running, to start using:
sudo service nginx start
2. PHP 8.3 Installation
PHP is a powerful open-source scripting language essential for building dynamic websites. Unlike HTML, which displays static content, PHP code is executed on the server. This allows for features like user interaction, database connections, and generating customized web pages.
- Similar to Engine-X, the official Ubuntu repositories might not offer the latest PHP version. We’ll use the repository maintained by Ondřej Surý for a more recent PHP 8.3 installation.
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
- Then install PHP 8.3, as well as all the PHP packages you will require:
sudo apt install php8.3-fpm php8.3-common php8.3-mysql \
php8.3-xml php8.3-intl php8.3-curl php8.3-gd \
php8.3-imagick php8.3-cli php8.3-dev php8.3-imap \
php8.3-mbstring php8.3-opcache php8.3-redis \
php8.3-soap php8.3-zip -y
During installation, you might see php-fpm included. This stands for FastCGI Process Manager (FPM). It’s a performance-enhancing tool that efficiently manages PHP code execution, particularly when paired with Engine-X. For setups, PHP-FPM is the recommended choice for optimal performance.
- Once the installation finishes, let’s test if PHP is working correctly.
php-fpm8.3 -v
newuser@tutorial:~$ php-fpm8.3 -v
PHP 8.3.9 (fpm-fcgi) (built: Jul 5 2024 12:04:09)
Copyright (c) The PHP Group
Zend Engine v4.3.9, Copyright (c) Zend Technologies
with Zend OPcache v8.3.9, Copyright (c), by Zend Technologies
3. Configure PHP 8.3 and PHP-FPM
By default, this setup configures a single PHP pool running under your user account. While this simplifies initial configuration, it doesn’t provide security isolation between websites.
For enhanced security, especially for production environments, we highly recommend using a dedicated system user for PHP-FPM. This creates isolation between websites and offers a more secure approach.
Alternative Approaches:
- Dedicated System User: Create a separate user account specifically for running PHP-FPM. This enhances security by isolating websites from each other and the system.
- PHP-FPM Pools (Advanced): For complex setups with multiple websites, consider configuring separate PHP-FPM pools for each site. This allows for granular control over PHP settings for each website.
Important: If security isolation is a concern, this single-pool setup under your user account is not recommended. Choose one of the alternative approaches mentioned above for a more secure configuration.
- Open the default PHP-FPM pool configuration file:
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
- Changing these lines to your username simplifies management but be aware this approach is only acceptable for development servers with a single user. For production environments with multiple users, using a dedicated system user for Engine-X is a more secure practice.
user = newuser
group = newuser
listen.owner = newuser
listen.group = newuser
Once you’ve made your edits, press Ctrl+X and then Y to confirm saving the PHP-FPM configuration file.
- To increase WordPress max upload size, edit php.ini and adjust the limit (remember, we have update Engine-X client_max_body_size before).
sudo nano /etc/php/8.3/fpm/php.ini
- Set the values in php.ini for upload_max_filesize (in File Upload Section) and post_max_size (in Data Handling Section) to match the upload size you configured in client_max_body_size.
upload_max_filesize = 64M
post_max_size = 64M
- Then, save your changes using Ctrl+X followed by Y. Before restarting PHP-FPM, ensure the configuration file is free of errors by running a syntax check.
sudo php-fpm8.3 -t
newuser@tutorial:~$ sudo php-fpm8.3 -t
[09-Jul-2024 20:29:18] NOTICE: configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful
- if the syntax check passed without errors, restart PHP-FPM with this command:
sudo service php8.3-fpm restart
- Let’s use htop to confirm Engine-X and PHP-FPM are running under the intended user:
htop
In htop, SHIFT + M to sort by memory (optional) and check user for both Engine-X and php-fpm processes (should match your username or dedicated user).
htop might show one root user instance for each process (spawning workers), but the rest should match your username (or dedicated user).
If the user in htop doesn’t match your configuration, revisit your Nginx and PHP-FPM configuration files and ensure both services are restarted.
4. Now, let’s test if Engine-X and PHP are working together
To verify Engine-X and PHP integration, you can enable a test script (details below). This script displays PHP information in your browser, confirming successful processing of PHP files.
- Within the default Engine-X site configuration file, uncomment the relevant section to enable PHP processing.
sudo nano /etc/nginx/sites-available/default
- Find the section which controls the PHP scripts.
# pass PHP scripts to FastCGI server
#
#location ~ \.php$ {
# include snippets/fastcgi-php.conf;
#
# # With php-fpm (or other unix sockets):
# fastcgi_pass unix:/run/php/php7.4-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
#}
- Since we’re using PHP-FPM, modify the uncommented section to reflect the appropriate configuration for this setup.
# pass PHP scripts to FastCGI server
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php-fpm (or other unix sockets):
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
- After modifying the section for PHP processing, save your changes using Ctrl+X followed by Y. Ensure the syntax is correct by running a test command before restarting Engine-X.
sudo nginx -t
- If the configuration test didn’t reveal any errors, proceed with restarting Engine-X:
sudo service nginx restart
- Create a file named info.php inside the default web root directory /var/www/html. This script will help us test the PHP integration.
cd /var/www/html
sudo nano info.php
- Paste the following PHP code into the info.php file. Save your changes using the familiar Ctrl+X, Y combination.
<?php phpinfo();
- Lastly, because you set the user directive in your nginx.conf file to the user you’re currently logged in with, give that user permissions on the info.php file.
sudo chown newuser info.php
- Visit http://your_domain/info.php (replace your_domain with your website address) in your browser. If you see the PHP information screen, Engine-X is successfully processing PHP files! This confirms a working integration between Engine-X and PHP.
e.g., http://tutorial.caklus.com/info.php
- After successful testing, you can delete the info.php file as it’s no longer needed.
sudo rm /var/www/html/info.php
5. Set up a default server block to handle any requests not directed to a specific website.
Right now, if you visit an address on the server that isn’t set up yet, you’ll see the Engine-X welcome message. It would be cleaner if the server didn’t show anything instead.
- To prevent the Engine-X welcome page from displaying, start by deleting the following default site configuration files:
sudo rm /etc/nginx/sites-available/default
sudo rm /etc/nginx/sites-enabled/default
- Next, let’s add a default setting to Engine-X that catches any requests that aren’t for specific websites. To do this, you’ll need to make some changes to the nginx.conf file.
sudo nano /etc/nginx/nginx.conf
- Somewhere towards the end of the file, you’ll find a line that says:
include /etc/nginx/sites-enabled/*;
- To capture all unconfigured requests, add the following code block right after this line:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
- After you’re done editing, save the file by pressing the CTRL + X key. Then, press Y to confirm you want to save. Finally, let’s test the Engine-X configuration to make sure everything works properly.
sudo nginx -t
- If there are no errors in the test, restart Engine-X so the changes you made work.
sudo service nginx restart
- After restarting Engine-X, accessing your domain name should now result in an HTTP error code.
- This is the final nginx.conf configuration file after implementing the catch-all block and removing the unused mail block.
user newuser;
worker_processes 1;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
multi_accept on;
}
http {
##
# Basic Settings
##
keepalive_timeout 15;
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
server_tokens off;
client_max_body_size 64m;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
}
6. Install the WP-CLI command-line tool
If you’re unfamiliar with WP-CLI, it’s a powerful command-line interface for managing WordPress. It simplifies tasks like WordPress installation and streamlines various workflows.
- Go to your main directory on the server
cd ~/
- Download WP-CLI with the cURL command-line tool
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
- To verify the installation, run the following command:
php wp-cli.phar --info
If the command runs successfully, it will display information about your current PHP version and some additional details.
- For easier access, let’s make WP-CLI available from any directory in your terminal. To achieve this, we need to add it to your server’s PATH environment variable and ensure it has permission to execute.
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
All set! Use the power of WP-CLI by typing wp in your terminal.
NAME
wp
DESCRIPTION
Manage WordPress through the command-line.
SYNOPSIS
wp <command>
SUBCOMMANDS
cache Adds, removes, fetches, and flushes the WP Object Cache object.
cap Adds, removes, and lists capabilities of a user role.
cli Reviews current WP-CLI info, checks for updates, or views defined aliases.
comment Creates, updates, deletes, and moderates comments.
config Generates and reads the wp-config.php file.
core Downloads, installs, updates, and manages a WordPress installation.
cron Tests, runs, and deletes WP-Cron events; manages WP-Cron schedules.
db Performs basic database operations using credentials stored in wp-config.php.
embed Inspects oEmbed providers, clears embed cache, and more.
eval Executes arbitrary PHP code.
eval-file Loads and executes a PHP file.
export Exports WordPress content to a WXR file.
help Gets help on WP-CLI, or on a specific command.
i18n Provides internationalization tools for WordPress projects.
import Imports content from a given WXR file.
language Installs, activates, and manages language packs.
maintenance-mode Activates, deactivates or checks the status of the maintenance mode of a site.
media Imports files as attachments, regenerates thumbnails, or lists registered image
sizes.
7. Install the MySQL database server
The last piece of the puzzle is MySQL. Fortunately, Ubuntu provides a MySQL package directly through its official repositories.
- Run the following command to install MySQL.
sudo apt install mysql-server -y
By default, Ubuntu’s MySQL installation doesn’t require a password for the root user. This can be a security risk. To avoid issues, let’s quickly change the root user’s authentication method to use a password.
- First, open your terminal and connect to the MySQL prompt.
sudo mysql
- Use this command to switch the root user’s authentication method to the secure caching_sha2_password and set a password:
ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'password';
- Once you’ve set the password, exit the MySQL prompt.
exit
- Great! With the root user password set, it’s now safe to run the MySQL security script.
sudo mysql_secure_installation
- The security script will ask you some questions. Follow the on-screen prompts and enter the strong password you just set for the root user when prompted.
newuser@tutorial:~$ sudo mysql_secure_installation
Securing the MySQL server deployment.
Enter password for user root: ********
VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?
Press y|Y for Yes, any other key for No: Y
There are three levels of password validation policy:
LOW Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary file
Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
Using existing password for root.
Estimated strength of the password: 50
Change the password for root ? ((Press y|Y for Yes, any other key for No) : Y
New password: ********
Re-enter new password: ********
Estimated strength of the password: 100
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : Y
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.
Remove anonymous users? (Press y|Y for Yes, any other key for No) : Y
Success.
Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : Y
Success.
By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : Y
- Dropping test database...
Success.
- Removing privileges on test database...
Success.
Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : Y
Success.
All done!
Now that you have a secure foundation, let’s get your WordPress journey started! The next chapter will guide you through setting up your first WordPress site and unveil powerful techniques for managing multiple installations.