WordPress is the world's most popular content management system¹, used by more than 43% of all websites. Debian, as one of the most stable Linux distributions², provides a reliable foundation for hosting WordPress. This comprehensive guide covers the entire process of deploying WordPress on Debian using the LEMP stack³ (Linux, Nginx, MariaDB/MySQL, PHP), from initial system setup to advanced optimization and security methods.
¹ CMS (Content Management System) - a program that allows creating and editing websites without programming knowledge
² Linux distribution - an operating system based on the Linux kernel with a set of programs and utilities
³ LEMP stack - a set of software for web development: Linux (operating system), Nginx (web server), MySQL/MariaDB (database), PHP (programming language)
Linux root terminal session showing directory navigation, ping test, and package search commands relevant to server management.
System Preparation and Prerequisites
System Requirements
Before starting WordPress installation on Debian, you need to ensure your system meets the minimum requirements. For stable WordPress operation, it's recommended to have at least 1 GB of RAM, 20 GB of disk space, and a stable internet connection.
To begin, update your system to the latest version through the terminal⁴:
⁴ Terminal (command line) - a text interface for interacting with the operating system through commands
sudo apt update && sudo apt upgrade -y
# sudo - execute command with administrator privileges
# apt update - update the list of available packages
# && - execute next command only if previous one was successful
# apt upgrade -y - install all available updates without prompts
This command first updates the list of available packages⁵ (apt update), then installs updates (apt upgrade).
⁵ Package - a pre-compiled program ready for installation
Check the Debian version using the command:
cat /etc/debian_version
# cat - display file contents on screen
# /etc/debian_version - file containing Debian version
Network and Firewall Configuration
Security configuration begins with proper network connections and firewall⁶ setup. Install UFW (Uncomplicated Firewall) for basic protection:[4]
⁶ Firewall - a program that controls network traffic and blocks dangerous connections
sudo apt install ufw -y
# sudo apt install - install package with administrator privileges
# ufw - firewall package name
# -y - automatically answer "yes" to all questions
sudo ufw allow ssh
# sudo ufw allow - allow connections through firewall
# ssh - allow SSH connections (port 22)
sudo ufw allow 'Nginx Full'
# 'Nginx Full' - allow HTTP (port 80) and HTTPS (port 443) for Nginx
sudo ufw enable
# sudo ufw enable - activate firewall
These commands install the firewall, allow SSH⁷ connections and HTTP/HTTPS traffic for Nginx, then activate protection.
⁷ SSH (Secure Shell) - protocol for secure remote server access
LEMP Stack Installation
Installing Nginx Web Server
Diagram showing the components of the LEMP stack with Nginx as the web server, PHP-FPM as the scripting layer, and MariaDB/MySQL as the database layer, all running on the Linux operating system.
Installing Nginx on Debian:
⁸ Nginx - web server that processes HTTP requests from browsers and serves web pages
sudo apt install nginx -y
# sudo apt install - install package with administrator privileges
# nginx - Nginx web server package name
# -y - automatically confirm installation
After installation, start and enable Nginx for automatic startup at system boot:
sudo systemctl start nginx
# sudo systemctl start - start service
# nginx - service name to start
sudo systemctl enable nginx
# sudo systemctl enable - enable service autostart at boot
# nginx - service name to enable
Check service status⁹:
⁹ Service - a program that runs in the background and performs certain functions
sudo systemctl status nginx
# sudo systemctl status - check service status
# nginx - service name to check
You can test Nginx by visiting http://localhost or your server's IP address¹⁰ in a web browser.[1][3]
¹⁰ IP address - unique numerical identifier of a computer on the network
Installing MySQL/MariaDB
MariaDB¹¹ is a modern replacement for MySQL that provides better performance and additional features. For WordPress, it's recommended to use MariaDB:[5][6]
¹¹ MariaDB/MySQL - database management system that stores website information (articles, users, settings)
sudo apt install mariadb-server mariadb-client -y
# sudo apt install - install packages with administrator privileges
# mariadb-server - MariaDB server component
# mariadb-client - client utilities for connecting to MariaDB
# -y - automatically confirm installation
Start the database service:
sudo systemctl start mariadb
# sudo systemctl start - start service
# mariadb - MariaDB service name
sudo systemctl enable mariadb
# sudo systemctl enable - enable autostart at boot
# mariadb - service name to enable
Run MariaDB security setup:
sudo mysql_secure_installation
# sudo - execute with administrator privileges
# mysql_secure_installation - script for MariaDB security configuration
During setup, set a strong password for the root user¹² and answer "Y" to all security questions.[6][5]
¹² Root user - system administrator with full access privileges
Installing PHP and PHP-FPM
PHP-FPM¹³ (FastCGI Process Manager) is the optimal choice for working with Nginx, providing better performance and security. Install PHP along with necessary modules¹⁴:[2][3][7]
¹³ PHP-FPM - process manager for PHP that efficiently handles requests from web server
¹⁴ PHP modules - additional components that extend PHP capabilities for working with databases, images, etc.
sudo apt install php php-fpm php-mysql php-cli php-common php-zip php-curl php-mbstring php-xml php-gd php-intl php-bcmath -y
# sudo apt install - install packages with administrator privileges
# php - main PHP package
# php-fpm - FastCGI Process Manager for PHP
# php-mysql - module for working with MySQL/MariaDB databases
# php-cli - command line interface for PHP
# php-common - common files for PHP
# php-zip - module for working with ZIP archives
# php-curl - module for HTTP requests
# php-mbstring - module for working with multibyte strings
# php-xml - module for working with XML
# php-gd - module for working with images
# php-intl - internationalization module
# php-bcmath - module for precise mathematical calculations
# -y - automatically confirm installation
Check PHP version:
php -v
# php - run PHP
# -v - show PHP version
Start and enable PHP-FPM:
sudo systemctl start php8.2-fpm # replace with your PHP version
# sudo systemctl start - start service
# php8.2-fpm - PHP-FPM service name (version may differ)
sudo systemctl enable php8.2-fpm
# sudo systemctl enable - enable autostart at boot
# php8.2-fpm - service name to enable
Database Configuration for WordPress
Creating Database
Log into MySQL/MariaDB as root user:
sudo mysql -u root -p
# sudo - execute with administrator privileges
# mysql - client for connecting to MariaDB
# -u root - connect as root user
# -p - prompt for password when connecting
Create a separate database¹⁵ for WordPress:[5][6]
¹⁵ Database - structured data set that stores all website information
CREATE DATABASE wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CREATE DATABASE - command to create new database
-- wordpress_db - database name for WordPress
-- CHARACTER SET utf8mb4 - set UTF-8 encoding with emoji support
-- COLLATE utf8mb4_unicode_ci - set sorting rules for Unicode
This command creates a database named wordpress_db and configures UTF-8¹⁶ encoding to support all world languages.
¹⁶ UTF-8 - text encoding standard that supports symbols of all languages
Create a separate user for WordPress and grant appropriate privileges:
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'secure_password';
-- CREATE USER - command to create new user
-- 'wp_user' - username for WordPress
-- @'localhost' - user can connect only from local server
-- IDENTIFIED BY 'secure_password' - set password for user
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';
-- GRANT ALL PRIVILEGES - grant all access rights
-- ON wordpress_db.* - to all tables in wordpress_db database
-- TO 'wp_user'@'localhost' - to wp_user from localhost
FLUSH PRIVILEGES;
-- FLUSH PRIVILEGES - apply privilege changes
EXIT;
-- EXIT - exit MariaDB client
Replace secure_password with a strong password consisting of uppercase and lowercase letters, numbers, and special characters.[6][5]
Downloading and Installing WordPress
Downloading WordPress
Navigate to temporary directory¹⁷ and download the latest WordPress version:
¹⁷ Temporary directory (/tmp) - folder for storing files that will be deleted after reboot
cd /tmp
# cd - change current directory
# /tmp - go to temporary directory
wget https://wordpress.org/latest.tar.gz
# wget - utility for downloading files from Internet
# https://wordpress.org/latest.tar.gz - URL of latest WordPress version
The wget command downloads files from the Internet, and cd changes the current directory.
Extract¹⁸ the downloaded file:
¹⁸ Archive - compressed file containing multiple files and folders
tar xzvf latest.tar.gz
# tar - utility for working with archives
# x - extract files
# z - handle gzip compression
# v - show extraction process (verbose)
# f - specify archive file
# latest.tar.gz - archive file name to extract
Copy WordPress files to the web server's root directory¹⁹:
¹⁹ Web server root directory - folder where website files are stored (/var/www/html)
sudo cp -R wordpress/* /var/www/html/
# sudo - execute with administrator privileges
# cp - file copy command
# -R - copy recursively (including subfolders)
# wordpress/* - all files from wordpress folder
# /var/www/html/ - destination directory (web server root)
Set correct file permissions²⁰:[1][4]
²⁰ File permissions - access rights that determine who can read, write, or execute files
sudo chown -R www-data:www-data /var/www/html/
# sudo chown - change file ownership
# -R - recursively for all files and folders
# www-data:www-data - set owner and group to www-data
# /var/www/html/ - directory to change ownership
sudo find /var/www/html/ -type d -exec chmod 755 {} \;
# sudo find - find files and folders with administrator privileges
# /var/www/html/ - directory to search
# -type d - search only directories (folders)
# -exec chmod 755 {} \; - execute chmod 755 for each found folder
# 755 - permissions: owner can do everything, others can only read and execute
sudo find /var/www/html/ -type f -exec chmod 644 {} \;
# sudo find - find files with administrator privileges
# /var/www/html/ - directory to search
# -type f - search only files (not folders)
# -exec chmod 644 {} \; - execute chmod 644 for each found file
# 644 - permissions: owner can read and write, others can only read
These commands set file ownership (www-data - web server user) and correct permissions.
WordPress database configuration screen during installation wizard setup.
Creating Configuration File
Copy the sample configuration file:
cd /var/www/html/
# cd - change current directory
# /var/www/html/ - go to website root directory
sudo cp wp-config-sample.php wp-config.php
# sudo cp - copy file with administrator privileges
# wp-config-sample.php - WordPress sample configuration file
# wp-config.php - main WordPress configuration file
Edit the configuration file using a text editor²¹:
²¹ Text editor (nano) - program for editing text files in terminal
sudo nano wp-config.php
# sudo nano - open text editor with administrator privileges
# wp-config.php - file to edit
Change the following lines according to the created database:
define('DB_NAME', 'wordpress_db');
// define - PHP function to define constant
// 'DB_NAME' - constant name for database name
// 'wordpress_db' - constant value (your database name)
define('DB_USER', 'wp_user');
// 'DB_USER' - constant for database username
// 'wp_user' - username created for WordPress
define('DB_PASSWORD', 'secure_password');
// 'DB_PASSWORD' - constant for database password
// 'secure_password' - database user password
define('DB_HOST', 'localhost');
// 'DB_HOST' - constant for database server address
// 'localhost' - database is on the same server
Configuring Nginx Server Blocks
Creating Server Block
Nginx uses Server Blocks²² to serve multiple domains²³.[8][9]
Example of an Nginx server block configuration specifying server name, document root, index files, and try_files directive on CentOS 7.
Create a new configuration file for your domain:
²² Server Block - Nginx configuration for individual website or domain
²³ Domain - website name on the Internet (e.g., example.com)
sudo nano /etc/nginx/sites-available/your-domain.com
# sudo nano - open editor with administrator privileges
# /etc/nginx/sites-available/ - directory for website configurations
# your-domain.com - configuration file name (replace with your domain)
Example of an Nginx server block configuration specifying server name, document root, index files, and try_files directive on CentOS 7.
Add the following configuration for WordPress with Nginx:
server {
# server - start of server configuration block
listen 80;
# listen 80 - listen for HTTP requests on port 80
listen [::]:80;
# listen [::]:80 - listen for HTTP requests on port 80 for IPv6
server_name your-domain.com www.your-domain.com;
# server_name - domain names served by this block
root /var/www/html;
# root - website root directory
index index.php index.html index.htm;
# index - index files that Nginx looks for by default
# Logging
access_log /var/log/nginx/your-domain.com-access.log;
# access_log - file for recording visit logs
error_log /var/log/nginx/your-domain.com-error.log;
# error_log - file for recording error logs
# Main location block
location / {
# location / - handle all URLs starting with /
try_files $uri $uri/ /index.php$is_args$args;
# try_files - try to find file, then directory, otherwise pass to PHP
# $uri - requested URI
# $uri/ - try as directory
# /index.php$is_args$args - pass to index.php with parameters
}
# Processing PHP files through PHP-FPM
location ~ \.php$ {
# location ~ \.php$ - process all files ending with .php
include snippets/fastcgi-php.conf;
# include - include additional configuration file
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
# fastcgi_pass - pass request to PHP-FPM through Unix socket
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_param - set parameter for PHP-FPM
# SCRIPT_FILENAME - full path to PHP file
include fastcgi_params;
# include fastcgi_params - include standard FastCGI parameters
}
# Deny access to system files
location ~ /\.ht {
# location ~ /\.ht - block for files starting with .ht
deny all;
# deny all - deny access to everyone
}
# Static files optimization
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
# location ~* - case-insensitive matching for static files
expires 1y;
# expires 1y - set caching period to 1 year
add_header Cache-Control "public, immutable";
# add_header - add HTTP caching header
log_not_found off;
# log_not_found off - don't log missing static files
}
# WordPress specific settings
location = /favicon.ico {
# location = /favicon.ico - exact match for favicon
log_not_found off;
# don't log missing favicon
access_log off;
# don't log favicon requests
}
location = /robots.txt {
# location = /robots.txt - exact match for robots.txt
allow all;
# allow all - allow access to everyone
log_not_found off;
# don't log if file not found
access_log off;
# don't log robots.txt requests
}
# Protection from access to configuration files
location ~* \.(conf|engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|\.php_
{
# complex regex to protect system files
deny all;
# deny all - deny access to these files
}
}
This configuration defines how Nginx handles requests to your website.
Activate the new site by creating a symbolic link²⁴:
²⁴ Symbolic link - shortcut that points to another file or folder
sudo ln -s /etc/nginx/sites-available/your-domain.com /etc/nginx/sites-enabled/
# sudo ln - create link with administrator privileges
# -s - create symbolic link
# /etc/nginx/sites-available/your-domain.com - link source
# /etc/nginx/sites-enabled/ - directory for active sites
sudo nginx -t
# sudo nginx -t - check Nginx configuration syntax
sudo systemctl reload nginx
# sudo systemctl reload - reload Nginx configuration
Remove the default site:
sudo unlink /etc/nginx/sites-enabled/default
# sudo unlink - remove symbolic link
# /etc/nginx/sites-enabled/default - default Nginx configuration
Configuring PHP-FPM for Optimal Performance
Creating Separate Pool for WordPress
PHP-FPM allows creating separate pools²⁵ for better resource management. Create a dedicated pool for WordPress:[7]
²⁵ PHP-FPM pool - group of PHP processes for serving one or multiple websites
sudo nano /etc/php/8.2/fpm/pool.d/wordpress.conf
# sudo nano - open editor with administrator privileges
# /etc/php/8.2/fpm/pool.d/ - directory for PHP-FPM pool configurations
# wordpress.conf - pool configuration file for WordPress
Add the following configuration:
[wordpress]
; [wordpress] - PHP-FPM pool name
user = www-data
; user - user under which PHP processes run
group = www-data
; group - user group for PHP processes
listen = /run/php/php8.2-fpm-wordpress.sock
; listen - socket for connection with Nginx
listen.owner = www-data
; listen.owner - socket file owner
listen.group = www-data
; listen.group - socket file owner group
listen.mode = 0660
; listen.mode - socket file access permissions
pm = dynamic
; pm - process management mode (dynamic = dynamic)
pm.max_children = 20
; pm.max_children - maximum number of child processes
pm.start_servers = 4
; pm.start_servers - number of processes at startup
pm.min_spare_servers = 2
; pm.min_spare_servers - minimum number of idle processes
pm.max_spare_servers = 6
; pm.max_spare_servers - maximum number of idle processes
pm.max_requests = 500
; pm.max_requests - after how many requests to restart process
; PHP settings
php_admin_value[memory_limit] = 256M
; memory_limit - maximum memory for one PHP script
php_admin_value[max_execution_time] = 300
; max_execution_time - maximum script execution time in seconds
php_admin_value[upload_max_filesize] = 64M
; upload_max_filesize - maximum file size for upload
php_admin_value[post_max_size] = 64M
; post_max_size - maximum POST request size
; Security
php_admin_flag[allow_url_fopen] = off
; allow_url_fopen - disable opening URLs as files (security)
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
; disable_functions - disable dangerous PHP functions
These settings define how many PHP processes to start and what limits to set.
Update Nginx configuration to use the new pool:
location ~ \.php$ {
# location ~ \.php$ - block for processing PHP files
include snippets/fastcgi-php.conf;
# include - include standard FastCGI configuration
fastcgi_pass unix:/run/php/php8.2-fpm-wordpress.sock;
# fastcgi_pass - pass request to new WordPress pool
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_param - set script path parameter
include fastcgi_params;
# include - include standard FastCGI parameters
}
Restart PHP-FPM:
sudo systemctl restart php8.2-fpm
# sudo systemctl restart - restart service
# php8.2-fpm - PHP-FPM service name
Completing WordPress Installation
Web Interface Setup
Open a web browser and navigate to your domain address. You will see the WordPress initial setup screen.
WordPress database configuration screen during installation wizard setup.
WordPress setup configuration page for entering database details during installation.
WordPress setup configuration page for entering database details during installation.
Select interface language and enter site information:
-
Site name
-
Administrator username
-
Strong password
-
Administrator email address
After completing setup, you can log into the WordPress admin panel²⁶ at http://your-domain.com/wp-admin/.
²⁶ Admin panel - web interface for managing website, adding content and settings
WordPress Security Configuration
Protecting wp-config.php File
The wp-config.php file contains critically important information about your site. Unlike other web servers, Nginx doesn't use .htaccess files²⁷, so protection is configured directly in the server block:[10][11]
²⁷ .htaccess files - configuration files for Apache web servers, Nginx doesn't support them
# Deny access to wp-config.php
location ~* wp-config\.php {
# location ~* wp-config\.php - case-insensitive match for wp-config.php
deny all;
# deny all - deny access to all users
}
# Deny access to logs and configuration files
location ~* \.(log|conf)$ {
# location ~* \.(log|conf)$ - files with .log and .conf extensions
deny all;
# deny all - deny access to these files
}
Hiding Nginx Version
Add to main Nginx configuration:
sudo nano /etc/nginx/nginx.conf
# sudo nano - open editor with administrator privileges
# /etc/nginx/nginx.conf - main Nginx configuration file
http {
# http - main HTTP configuration block
# Hide Nginx version
server_tokens off;
# server_tokens off - don't show Nginx version in headers
# Additional security headers
add_header X-Frame-Options SAMEORIGIN;
# X-Frame-Options SAMEORIGIN - allow frames only from same domain
add_header X-Content-Type-Options nosniff;
# X-Content-Type-Options nosniff - prevent browser from guessing content type
add_header X-XSS-Protection "1; mode=block";
# X-XSS-Protection - protection from XSS attacks
add_header Referrer-Policy "strict-origin-when-cross-origin";
# Referrer-Policy - control referrer header transmission
}
These headers²⁸ protect against various types of attacks.
²⁸ HTTP headers - additional information transmitted with web pages to configure browser behavior
Limiting Login Attempts at Nginx Level
Create request rate limiting zones²⁹:
²⁹ Rate limiting - limiting number of requests per time period to protect from attacks
http {
# http - main HTTP configuration block
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=1r/s;
# limit_req_zone - create request limiting zone
# $binary_remote_addr - grouping key (client IP address)
# zone=wp_login:10m - zone name and memory size (10 megabytes)
# rate=1r/s - allow 1 request per second
limit_req_zone $binary_remote_addr zone=wp_admin:10m rate=5r/s;
# zone=wp_admin:10m - zone for admin panel
# rate=5r/s - allow 5 requests per second for admin panel
}
server {
# server - server configuration block
# Limits for wp-login.php
location = /wp-login.php {
# location = /wp-login.php - exact match for login page
limit_req zone=wp_login burst=2 nodelay;
# limit_req - apply limits
# zone=wp_login - use wp_login zone
# burst=2 - allow burst load up to 2 requests
# nodelay - don't delay allowed requests
include snippets/fastcgi-php.conf;
# include - include standard FastCGI configuration
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
# fastcgi_pass - pass request to PHP-FPM
}
# Limits for wp-admin
location ^~ /wp-admin/ {
# location ^~ /wp-admin/ - URLs starting with /wp-admin/
limit_req zone=wp_admin burst=5 nodelay;
# apply limits for admin panel with burst load up to 5
}
}
SSL/TLS Configuration with Let's Encrypt
Installing Certbot for Nginx
Let's Encrypt³⁰ provides free SSL certificates³¹:[12][13]
³⁰ Let's Encrypt - non-profit organization that issues free SSL certificates
³¹ SSL certificate - digital document that ensures encrypted connection between browser and server
sudo apt install python3-certbot-nginx -y
# sudo apt install - install package with administrator privileges
# python3-certbot-nginx - Certbot client for Nginx
# -y - automatically confirm installation
Obtaining SSL Certificate
Run Certbot for automatic SSL configuration:
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
# sudo certbot - run Certbot with administrator privileges
# --nginx - use Nginx plugin
# -d your-domain.com - domain for certificate
# -d www.your-domain.com - additional domain (with www)
Certbot automatically modifies Nginx configuration, adding SSL settings:
server {
# server - HTTPS server configuration block
listen 443 ssl http2;
# listen 443 - listen for HTTPS on port 443
# ssl - activate SSL
# http2 - HTTP/2 protocol support
listen [::]:443 ssl http2;
# IPv6 support for HTTPS
server_name your-domain.com www.your-domain.com;
# server_name - domains for this block
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
# ssl_certificate - path to SSL certificate
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# ssl_certificate_key - path to SSL private key
# SSL optimization
ssl_session_cache shared:SSL:10m;
# ssl_session_cache - SSL session cache (10MB shared cache)
ssl_session_timeout 10m;
# ssl_session_timeout - SSL session lifetime (10 minutes)
ssl_protocols TLSv1.2 TLSv1.3;
# ssl_protocols - supported TLS versions (only secure ones)
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
# ssl_ciphers - allowed encryption algorithms
ssl_prefer_server_ciphers off;
# ssl_prefer_server_ciphers off - allow client to choose cipher
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# HSTS - force browser to use HTTPS (2 years)
}
# HTTP to HTTPS redirect
server {
# server - block for redirecting HTTP traffic
listen 80;
# listen 80 - listen for HTTP requests
listen [::]:80;
# IPv6 support for HTTP
server_name your-domain.com www.your-domain.com;
# server_name - domains for redirect
return 301 https://$server_name$request_uri;
# return 301 - permanent redirect (code 301)
# https://$server_name$request_uri - to HTTPS version of same URL
}
Automatic Certificate Renewal
Configure automatic certificate renewal through cron³²:
³² Cron - task scheduler in Linux that allows automatically running commands on schedule
sudo crontab -e
# sudo crontab -e - edit task schedule for root user
Add the following line for daily renewal check at 12:00:
0 12 * * * /usr/bin/certbot renew --quiet
# 0 12 * * * - execution time (minute hour day month day_of_week)
# /usr/bin/certbot renew - certificate renewal command
# --quiet - quiet mode (no output if everything is fine)
Complete WordPress Optimization with Nginx
Configuring FastCGI Caching
FastCGI cache³³ is one of the most effective optimization methods for Nginx with PHP.
Nginx FastCGI cache architecture illustrating cache layers from browser to database and their impact on server response speed.
Add to Nginx configuration:
³³ FastCGI cache - mechanism for storing ready pages for faster serving of repeated requests
http {
# http - main HTTP configuration block
# FastCGI cache settings
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
# fastcgi_cache_path - path to cache directory
# /var/cache/nginx - directory for storing cache
# levels=1:2 - cache subfolder structure (1 level, 2 characters)
# keys_zone=WORDPRESS:100m - memory zone for cache keys (100MB)
# inactive=60m - remove inactive files after 60 minutes
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# fastcgi_cache_key - cache key (protocol+method+host+URI)
fastcgi_cache_use_stale error timeout invalid_header http_500;
# fastcgi_cache_use_stale - serve stale cache on errors
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
# fastcgi_ignore_headers - ignore these headers when caching
}
server {
# server - server configuration block
# Caching settings for PHP
location ~ \.php$ {
# location ~ \.php$ - block for PHP files
# Standard PHP-FPM configuration
include snippets/fastcgi-php.conf;
# include - include standard FastCGI configuration
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
# fastcgi_pass - pass request to PHP-FPM
# FastCGI cache
fastcgi_cache WORDPRESS;
# fastcgi_cache - use WORDPRESS cache zone
fastcgi_cache_valid 200 60m;
# fastcgi_cache_valid 200 60m - cache successful responses for 60 minutes
fastcgi_cache_valid 404 1m;
# cache 404 errors for 1 minute
# Adding cache headers
add_header X-Cache $upstream_cache_status;
# X-Cache - header with cache status (HIT/MISS/BYPASS)
# Skip caching for admin panel and logged users
fastcgi_cache_bypass $skip_cache;
# fastcgi_cache_bypass - skip cache if $skip_cache = 1
fastcgi_no_cache $skip_cache;
# fastcgi_no_cache - don't store in cache if $skip_cache = 1
}
# Variables for cache management
set $skip_cache 0;
# set $skip_cache 0 - allow caching by default
# Skip caching for POST requests
if ($request_method = POST) {
# if - condition to check request method
set $skip_cache 1;
# set cache skip flag for POST requests
}
# Skip caching for URLs with parameters
if ($query_string != "") {
# if - check if there are GET parameters in URL
set $skip_cache 1;
# don't cache pages with parameters
}
# Skip caching for admin panel
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
# if - check URI with regular expression
# ~* - case-insensitive matching
set $skip_cache 1;
# don't cache admin pages and system files
}
# Skip caching for logged users
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
# if - check cookies for authorization markers
set $skip_cache 1;
# don't cache for logged users
}
}
Create cache directory:
sudo mkdir -p /var/cache/nginx
# sudo mkdir - create directory with administrator privileges
# -p - create all necessary parent directories
# /var/cache/nginx - path to cache directory
sudo chown www-data:www-data /var/cache/nginx
# sudo chown - change directory ownership
# www-data:www-data - set owner and group to www-data
Static Files Optimization
Configure efficient caching for static files³⁴:
³⁴ Static files - images, CSS, JavaScript and other files that don't change dynamically
server {
# server - server configuration block
# Media files caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
# location ~* - case-insensitive matching for images and styles
expires 1y;
# expires 1y - set caching period to 1 year
add_header Cache-Control "public, immutable";
# Cache-Control - public cache, files are immutable
add_header Vary Accept-Encoding;
# Vary Accept-Encoding - cache different versions for compression
log_not_found off;
# don't log missing static files
}
# Font caching
location ~* \.(woff|woff2|ttf|svg|eot)$ {
# location ~* - block for web fonts
expires 1y;
# cache fonts for 1 year
add_header Cache-Control "public, immutable";
# public immutable cache for fonts
add_header Access-Control-Allow-Origin "*";
# allow font usage from other domains
}
# Gzip compression
gzip on;
# gzip on - activate gzip compression
gzip_vary on;
# gzip_vary - add Vary: Accept-Encoding header
gzip_min_length 1024;
# gzip_min_length - compress files larger than 1KB
gzip_comp_level 6;
# gzip_comp_level - compression level (1-9, 6 is optimal)
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json;
# gzip_types - file types for compression
}
Configuring Brotli Compression
For even better compression³⁵, install Brotli module:
³⁵ Brotli - modern compression algorithm that provides better results than Gzip
sudo apt install nginx-module-brotli -y
# sudo apt install - install package with administrator privileges
# nginx-module-brotli - Brotli module for Nginx
# -y - automatically confirm installation
Add to Nginx configuration:
load_module modules/ngx_http_brotli_filter_module.so;
# load_module - load Brotli filter module
load_module modules/ngx_http_brotli_static_module.so;
# load_module - load module for pre-compressed files
http {
# http - main HTTP configuration block
# Brotli compression
brotli on;
# brotli on - activate Brotli compression
brotli_comp_level 6;
# brotli_comp_level - Brotli compression level (0-11, 6 is balanced)
brotli_types
text/plain
text/css
application/json
application/javascript
text/xml
application/xml
application/xml+rss
text/javascript;
# brotli_types - file types for Brotli compression
}
PHP-FPM Optimization
Configure optimal parameters for your server in /etc/php/8.2/fpm/php.ini file:
sudo nano /etc/php/8.2/fpm/php.ini
# sudo nano - open editor with administrator privileges
# /etc/php/8.2/fpm/php.ini - PHP configuration file
; Memory optimization
memory_limit = 256M
; memory_limit - maximum memory for PHP script (256 megabytes)
max_execution_time = 300
; max_execution_time - maximum script execution time (5 minutes)
max_input_vars = 3000
; max_input_vars - maximum number of input variables
; OPcache optimization
opcache.enable=1
; opcache.enable - activate OPcache
opcache.memory_consumption=128
; opcache.memory_consumption - memory for OPcache (128MB)
opcache.interned_strings_buffer=8
; opcache.interned_strings_buffer - string buffer (8MB)
opcache.max_accelerated_files=4000
; opcache.max_accelerated_files - maximum number of cached files
opcache.revalidate_freq=2
; opcache.revalidate_freq - check file changes every 2 seconds
opcache.fast_shutdown=1
; opcache.fast_shutdown - fast shutdown
opcache.enable_cli=1
; opcache.enable_cli - activate OPcache for command line
; File uploads
upload_max_filesize = 64M
; upload_max_filesize - maximum file size for upload
post_max_size = 64M
; post_max_size - maximum POST request size
; Sessions
session.save_handler = files
; session.save_handler - store sessions in files
session.save_path = "/tmp"
; session.save_path - directory for session files
OPcache³⁶ significantly speeds up PHP script execution.
³⁶ OPcache - mechanism for caching compiled PHP code to speed up execution
Flowchart showing opcode caching process in PHP scripts, illustrating how precompiled code improves execution efficiency important for WordPress performance optimization.
Nginx Performance Monitoring
Activate Nginx statistics module:
sudo nano /etc/nginx/sites-available/your-domain.com
# sudo nano - open site configuration for editing
Add location for statistics:
location /nginx_status {
# location /nginx_status - block for Nginx statistics
stub_status;
# stub_status - activate statistics module
allow 127.0.0.1;
# allow 127.0.0.1 - allow access only from localhost
deny all;
# deny all - deny access to everyone else
}
Check statistics:
curl http://localhost/nginx_status
# curl - utility for HTTP requests
# http://localhost/nginx_status - Nginx statistics URL
Clearing FastCGI Cache
Create a script³⁷ for clearing cache:
³⁷ Script - automated sequence of commands
sudo nano /usr/local/bin/clear-nginx-cache.sh
# sudo nano - create new script file with administrator privileges
# /usr/local/bin/ - directory for local scripts
#!/bin/bash
# #!/bin/bash - specify interpreter for script execution
sudo rm -rf /var/cache/nginx/*
# sudo rm - delete files with administrator privileges
# -rf - recursively and forcefully
# /var/cache/nginx/* - all Nginx cache files
sudo systemctl reload nginx
# sudo systemctl reload - reload Nginx configuration
echo "Nginx FastCGI cache cleared"
# echo - output message about successful clearing
Make script executable:
sudo chmod +x /usr/local/bin/clear-nginx-cache.sh
# sudo chmod - change file permissions
# +x - add execution permission
# /usr/local/bin/clear-nginx-cache.sh - path to script
Backup and Maintenance
Backup Strategy
Create script for automatic backup including Nginx configuration:
sudo nano /usr/local/bin/wp-backup.sh
# sudo nano - create new backup script
#!/bin/bash
# #!/bin/bash - bash interpreter for script execution
# Variables
DB_NAME="wordpress_db"
# DB_NAME - WordPress database name
DB_USER="wp_user"
# DB_USER - database user
DB_PASSWORD="secure_password"
# DB_PASSWORD - database password
WP_PATH="/var/www/html"
# WP_PATH - path to WordPress files
NGINX_PATH="/etc/nginx"
# NGINX_PATH - path to Nginx configuration
BACKUP_PATH="/backups/wordpress"
# BACKUP_PATH - directory for storing backups
DATE=$(date +%Y%m%d_%H%M%S)
# DATE - current date and time for file name
# Create backup directory
mkdir -p $BACKUP_PATH
# mkdir -p - create directory and all parent directories
# Database backup
mysqldump -u$DB_USER -p$DB_PASSWORD $DB_NAME > $BACKUP_PATH/database_$DATE.sql
# mysqldump - MySQL/MariaDB database export utility
# > - redirect output to file
# WordPress files backup
tar -czf $BACKUP_PATH/wordpress_files_$DATE.tar.gz -C $WP_PATH .
# tar - archiving utility
# -czf - create gzip archive from file
# -C - change directory before archiving
# Nginx configuration backup
tar -czf $BACKUP_PATH/nginx_config_$DATE.tar.gz -C $NGINX_PATH .
# archive Nginx configuration files
# Delete old backups (older than 30 days)
find $BACKUP_PATH -type f -name "*.sql" -mtime +30 -delete
# find - find files older than 30 days and delete them
# -type f - only files
# -mtime +30 - modified more than 30 days ago
# -delete - delete found files
find $BACKUP_PATH -type f -name "*.tar.gz" -mtime +30 -delete
# delete old archives
echo "Backup completed: $DATE"
# echo - output completion message
Nginx Log Monitoring
Configure log rotation³⁸ for better management:
³⁸ Log rotation - automatic archiving and deletion of old log files to save disk space
sudo nano /etc/logrotate.d/nginx-wordpress
# sudo nano - create log rotation configuration
/var/log/nginx/*.log {
# /var/log/nginx/*.log - path to all Nginx log files
daily
# daily - perform rotation daily
missingok
# missingok - don't error if file is missing
rotate 30
# rotate 30 - keep 30 old log versions
compress
# compress - compress old log files
delaycompress
# delaycompress - don't compress immediately, but on next rotation
notifempty
# notifempty - don't rotate empty files
create 0644 www-data adm
# create - create new log file with permissions 0644, owner www-data, group adm
sharedscripts
# sharedscripts - execute scripts only once for all files
prerotate
# prerotate - commands before rotation
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi
# execute additional scripts if they exist
endscript
postrotate
# postrotate - commands after rotation
invoke-rc.d nginx rotate >/dev/null 2>&1
# reload Nginx to create new log files
endscript
}
Troubleshooting Common Issues
502 Bad Gateway Error
This error³⁹ often occurs due to PHP-FPM issues. Check:[14][15]
³⁹ 502 Bad Gateway - error that occurs when Nginx cannot connect to PHP-FPM
-
PHP-FPM status:
sudo systemctl status php8.2-fpm
# sudo systemctl status - check PHP-FPM service status
-
Correct socket path in Nginx configuration
-
PHP-FPM socket permissions⁴⁰:
⁴⁰ Socket - file for communication between Nginx and PHP-FPM
ls -la /run/php/
# ls - show file list with details
# -la - show all files with detailed information
# /run/php/ - directory with PHP-FPM sockets
413 Request Entity Too Large Error
Increase upload limits in Nginx:
server {
# server - server configuration block
client_max_body_size 64M;
# client_max_body_size - maximum request size from client (64MB)
}
Permalink Issues
Nginx requires manual configuration of rewrite rules⁴¹. Make sure your configuration has:
⁴¹ Rewrite rules - instructions for URL redirection
location / {
# location / - main block for all URLs
try_files $uri $uri/ /index.php$is_args$args;
# try_files - try file, directory, otherwise pass to PHP
# $uri - requested URI
# $uri/ - try as directory
# /index.php$is_args$args - pass to index.php with parameters
}
FastCGI Cache Issues
If caching problems occur:
-
Check cache directory permissions:
sudo chown -R www-data:www-data /var/cache/nginx
# sudo chown - change ownership recursively for all files
# www-data:www-data - set owner and group
sudo chmod -R 755 /var/cache/nginx
# sudo chmod - change permissions recursively
# 755 - permissions: owner can do everything, others can read and execute
-
Clear cache:
sudo rm -rf /var/cache/nginx/*
# sudo rm - delete files with administrator privileges
# -rf - recursively and forcefully
# /var/cache/nginx/* - all cache files
sudo systemctl reload nginx
# sudo systemctl reload - reload configuration without stopping service
SSL Issues
Check SSL configuration:
sudo nginx -t
# sudo nginx -t - check Nginx configuration syntax
sudo certbot certificates
# sudo certbot certificates - show information about installed certificates
If certificate doesn't auto-renew:
sudo certbot renew --dry-run --nginx
# sudo certbot renew - renew certificates
# --dry-run - test mode without real changes
# --nginx - use Nginx plugin
Conclusion
Deploying WordPress on Debian with Nginx provides high performance and reliability thanks to the advantages of the LEMP stack. Nginx demonstrates significantly better performance metrics, especially under high load, due to its efficient event-driven architecture.[1][2]
Diagram showing the components of the LEMP stack with Nginx as the web server, PHP-FPM as the scripting layer, and MariaDB/MySQL as the database layer, all running on the Linux operating system.
Key Advantages of Nginx for WordPress:
Performance: Nginx consumes less memory and CPU resources, handling thousands of concurrent connections with minimal resource overhead.[2]
Diagram showing the components of the LEMP stack with Nginx as the web server, PHP-FPM as the scripting layer, and MariaDB/MySQL as the database layer, all running on the Linux operating system.
FastCGI caching provides significant website acceleration by reducing load on PHP and the database.
Scalability⁴²: Nginx architecture allows easy application scaling to serve millions of requests per day without significant performance degradation.[1][3]
⁴² Scalability - system's ability to handle growing load by adding resources
Security: Centralized configuration management at the server level increases security since all settings are controlled by the administrator.[16]
Flexibility: Nginx Server Blocks provide more configuration options, allowing creation of complex routing and request processing rules.[8][9]
A properly configured WordPress with Nginx on Debian can provide response times under 100 milliseconds even under high load.
Nginx FastCGI cache architecture illustrating cache layers from browser to database and their impact on server response speed.
Regular maintenance, monitoring, and updates ensure your site remains fast, secure, and stable over time.
⁂
- https://www.rosehosting.com/blog/how-to-install-wordpress-with-nginx-on-debian-10/
- https://spinupwp.com/hosting-wordpress-yourself-nginx-php-mysql/
- https://www.linuxtuto.com/how-to-install-wordpress-on-debian-12/
- https://linuxcapable.com/how-to-install-wordpress-with-nginx-on-debian-linux/
- https://mariadb.com/resources/blog/how-to-install-and-run-wordpress-with-mariadb/
- https://computingforgeeks.com/install-wordpress-with-nginx-on-ubuntu-debian/
- https://www.digitalocean.com/community/tutorials/php-fpm-nginx
- https://spinupwp.com/hosting-wordpress-yourself-setting-up-sites/
- https://www.digitalocean.com/community/tutorials/install-wordpress-nginx-ubuntu
- https://melapress.com/secure-wp-config-php-file/
- https://www.malcare.com/blog/secure-site-with-wp-config/
- https://snapshooter.com/learn/guides/install-wordpress
- https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-debian-10
- https://www.rosehosting.com/blog/how-to-solve-the-most-common-wordpress-errors/
- https://docs.vultr.com/common-wordpress-troubleshooting-guide
- https://developer.wordpress.org/advanced-administration/server/web-server/nginx/
en