📋 Overview
In this section, we will harden and optimize NGINX for production WordPress environments. The default NGINX configuration is already secure and fairly well optimized, making it straightforward to enhance performance and security with targeted modifications.
⚠️ Important Note
There is no one-size-fits-all configuration for all sites. You need to configure NGINX based on the specific requirements of your applications. This guide focuses on optimizing NGINX for WordPress sites.
📁 NGINX Configuration Architecture
Main Context (Global Settings)
Controls fundamental NGINX behavior affecting all operations
Events Context (Connection Handling)
Manages how NGINX handles client connections
HTTP Context (Web Server Settings)
Contains all HTTP-related configurations including:
- Basic Settings
- Buffer Configurations
- Timeout Settings
- Compression (Gzip/Brotli)
- File Handle Cache
Server Context (Virtual Hosts)
Individual site configurations (covered later in the course)
🛠️ Initial Setup: Creating Include Files
NGINX uses the include directive to modularize configurations, making them more manageable
and reusable across multiple sites.
Configuration Organization Flow
Step 1: Backup Original Configuration
cd /etc/nginx/ sudo cp nginx.conf nginx.conf.bak
Step 2: Create Includes Directory
sudo mkdir includes/ cd includes/
Step 3: Create Configuration Files
sudo touch basic_settings.conf buffers.conf timeouts.conf \
file_handle_cache.conf gzip.conf brotli.conf
⚙️ Main Context Configuration
Main Context Directives
The Main Context is the only context without curly brackets. It controls process-level settings for NGINX.
worker_rlimit_nofile 30000; worker_priority -10; timer_resolution 100ms; pcre_jit on;
- worker_rlimit_nofile 30000; Sets the maximum number of open file descriptors per worker process. This allows NGINX to handle more simultaneous connections and efficiently manage I/O operations.
- worker_priority -10; Assigns higher priority to NGINX worker processes (lower values = higher priority). This ensures NGINX processes are scheduled promptly by the kernel, especially under high load.
- timer_resolution 100ms; Sets timer resolution to 100 milliseconds, affecting timeout handling and event scheduling precision.
- pcre_jit on; Enables Just-In-Time compilation for Perl Compatible Regular Expressions, improving regex processing performance.
🔄 Events Context Configuration
Events Context Directives
The Events Context configures how NGINX handles connections at the process level.
events {
worker_connections 4096;
accept_mutex on;
accept_mutex_delay 200ms;
use epoll;
}
- worker_connections 4096; Maximum simultaneous connections per worker process. With multiple workers, total capacity = workers × connections.
- accept_mutex on; Synchronizes accept() calls among workers to prevent the "thundering herd" problem and distribute connections evenly.
- accept_mutex_delay 200ms; Delays accepting new connections by 200ms when mutex is enabled, improving load distribution.
- use epoll; Specifies the Linux epoll event notification mechanism for efficient handling of large numbers of connections.
Connection Handling Flow
📝 Basic Settings Configuration
File: /etc/nginx/includes/basic_settings.conf
## # BASIC SETTINGS ## charset utf-8; sendfile on; sendfile_max_chunk 512k; tcp_nopush on; tcp_nodelay on; server_tokens off; more_clear_headers 'Server'; more_clear_headers 'X-Powered'; server_name_in_redirect off; server_names_hash_bucket_size 64; variables_hash_max_size 2048; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream;
Key Directives Explained
| Directive | Purpose | Security/Performance Impact |
|---|---|---|
charset utf-8; |
Sets default character encoding | Ensures proper text rendering |
sendfile on; |
Uses kernel sendfile() system call | ⚡ Improves static file serving performance |
tcp_nopush on; |
Optimizes TCP packet transmission | ⚡ Reduces network overhead |
tcp_nodelay on; |
Disables Nagle's algorithm | ⚡ Improves small packet performance |
server_tokens off; |
Hides NGINX version number | 🔒 Security hardening |
💾 Buffer Configuration
File: /etc/nginx/includes/buffers.conf
Understanding NGINX Buffers
NGINX uses three types of buffers for efficient data handling:
- Input Buffers: Store client request bodies before upstream processing
- Output Buffers: Store response data before sending to clients
- FastCGI Buffers: Store responses from PHP-FPM or other FastCGI servers
## # BUFFERS ## client_body_buffer_size 256k; client_body_in_file_only off; client_header_buffer_size 64k; client_max_body_size 100m; connection_pool_size 512; directio 4m; ignore_invalid_headers on; large_client_header_buffers 8 64k; output_buffers 8 256k; postpone_output 1460; request_pool_size 32k;
Buffer Directives Breakdown
- client_body_buffer_size 256k; Buffer size for reading client request bodies. Larger requests are written to disk.
- client_max_body_size 100m; Maximum allowed size for client uploads. Set to 100MB to allow theme/plugin uploads. Reduce to 8MB after initial setup.
- large_client_header_buffers 8 64k; Number and size of buffers for large request headers (8 buffers of 64KB each).
- output_buffers 8 256k; Buffers for sending responses to clients (8 buffers of 256KB each).
- directio 4m; Enables direct I/O for files larger than 4MB, bypassing system cache for better performance.
⏱️ Timeout Configuration
File: /etc/nginx/includes/timeouts.conf
## # TIMEOUTS ## keepalive_timeout 5; keepalive_requests 500; lingering_time 20s; lingering_timeout 5s; keepalive_disable msie6; reset_timedout_connection on; send_timeout 15s; client_header_timeout 8s; client_body_timeout 10s;
Request Timeout Flow
client_header_timeout (8s) → Client must send headers within 8 seconds
client_body_timeout (10s) → Client must send request body within 10 seconds
send_timeout (15s) → Response must be sent within 15 seconds
keepalive_timeout (5s) → Keep connection alive for 5 seconds for reuse
Timeout Directives Explained
| Directive | Value | Purpose |
|---|---|---|
keepalive_timeout |
5 seconds | How long to keep idle connections open |
keepalive_requests |
500 | Max requests per keepalive connection |
reset_timedout_connection |
on | Reset timed-out connections to free resources |
send_timeout |
15 seconds | Timeout for transmitting response to client |
client_header_timeout |
8 seconds | Timeout for reading client request header |
client_body_timeout |
10 seconds | Timeout for reading client request body |
🗜️ Compression Configuration
File: /etc/nginx/includes/gzip.conf
##
# GZIP
##
gzip on;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
gzip_static on;
gzip_min_length 1400;
gzip_buffers 32 8k;
gzip_http_version 1.0;
gzip_comp_level 5;
gzip_proxied any;
gzip_types text/plain text/css text/xml application/javascript
application/x-javascript application/xml
application/xml+rss application/ecmascript
application/json image/svg+xml;
File: /etc/nginx/includes/brotli.conf
##
# BROTLI
##
brotli on;
brotli_comp_level 6;
brotli_static on;
brotli_types application/atom+xml application/javascript
application/json application/rss+xml
application/vnd.ms-fontobject application/x-font-opentype
application/x-font-truetype application/x-font-ttf
application/x-javascript application/xhtml+xml
application/xml font/eot font/opentype font/otf
font/truetype image/svg+xml image/vnd.microsoft.icon
image/x-icon image/x-win-bitmap text/css
text/javascript text/plain text/xml;
Compression Benefits
Gzip: Widely supported compression reducing bandwidth by 70-90% for text files
Brotli: Modern compression with 15-25% better compression than Gzip, supported by all modern browsers
📂 File Handle Cache Configuration
File: /etc/nginx/includes/file_handle_cache.conf
## # FILE HANDLE CACHE ## open_file_cache max=50000 inactive=60s; open_file_cache_valid 120s; open_file_cache_min_uses 2; open_file_cache_errors off;
File Handle Cache Explained
- open_file_cache max=50000 inactive=60s; Caches up to 50,000 file descriptors. Entries inactive for 60 seconds are removed.
- open_file_cache_valid 120s; Cached information is valid for 120 seconds before revalidation.
- open_file_cache_min_uses 2; Files must be accessed at least 2 times before being cached.
- open_file_cache_errors off; Do not cache file access errors.
🔗 Updating nginx.conf
Include All Configuration Files
After creating all include files, update /etc/nginx/nginx.conf to reference them:
http {
##
# Basic Settings
##
include /etc/nginx/includes/basic_settings.conf;
##
# Gzip Settings
##
include /etc/nginx/includes/gzip.conf;
##
# Brotli Settings
##
include /etc/nginx/includes/brotli.conf;
##
# Buffer Settings
##
include /etc/nginx/includes/buffers.conf;
##
# Timeout Settings
##
include /etc/nginx/includes/timeouts.conf;
##
# File Handle Cache Settings
##
include /etc/nginx/includes/file_handle_cache.conf;
# ... rest of configuration
}
✅ Testing and Applying Configuration
Test NGINX Configuration
sudo nginx -t
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Reload NGINX
sudo systemctl reload nginx
🔍 Verifying Open File Limits
Check NGINX Process Limits
# Find NGINX worker process ID ps aux | grep www-data # Check limits (replace XXX with actual PID) cat /proc/XXX/limits
Verify that "Max open files" shows the configured limit (30000 or higher).
Increasing Limits if Needed
If limits are lower than expected, update the main context:
worker_rlimit_nofile 45000;
Then reload NGINX and verify again.
⚡ Bash Aliases for Efficiency
Create Useful Aliases
# Edit bash aliases nano ~/.bash_aliases # Add these aliases: alias server_update='sudo apt update && sudo apt upgrade && sudo apt autoremove' alias ngt='sudo nginx -t' alias ngr='sudo systemctl reload nginx' alias fpmr='sudo systemctl restart php8.3-fpm' alias ngsa='cd /etc/nginx/sites-available/ && ls' alias ngin='cd /etc/nginx/includes/ && ls' # Activate aliases su andrew # Switch to your user to reload shell
Alias Benefits
ngt- Quick NGINX configuration testngr- Fast NGINX reloadfpmr- Restart PHP-FPM servicengsa- Navigate to sites-availablengin- Navigate to includes directory
🗄️ MariaDB Optimization
Secure MariaDB Installation
sudo mysql_secure_installation
Performance Schema Configuration
Edit MariaDB configuration:
cd /etc/mysql/mariadb.conf.d/ sudo cp 50-server.cnf 50-server.cnf.bak sudo nano 50-server.cnf
Add performance schema settings:
# Performance Schema performance_schema=ON performance-schema-instrument='stage/%=ON' performance-schema-consumer-events-stages-current=ON performance-schema-consumer-events-stages-history=ON performance-schema-consumer-events-stages-history-long=ON # Network optimization skip-name-resolve # Binary log retention expire_logs_days = 3
InnoDB Buffer Pool Optimization
For a 1GB RAM server:
innodb_buffer_pool_size = 800M innodb_log_file_size = 200M
Important: Stop MariaDB Before Changing InnoDB Settings
sudo systemctl stop mariadb sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf # Make changes sudo systemctl start mariadb
Verify InnoDB Settings
sudo mysql SHOW VARIABLES LIKE '%innodb_buffer%'; SHOW VARIABLES LIKE '%innodb_log%';
MariaDB Open File Limits
# Create systemd override directory cd /etc/systemd/system/ sudo mkdir mariadb.service.d/ cd mariadb.service.d/ # Create limits configuration sudo nano limits.conf
Add the following:
[Service] LimitNOFILE=40000
Apply changes:
sudo systemctl daemon-reload sudo systemctl restart mariadb # Verify ps aux | grep mysql cat /proc/XXX/limits # Replace XXX with PID
🐘 PHP-FPM Optimization
PHP Configuration Override
cd /etc/php/8.3/fpm/conf.d/ sudo nano server_override.ini
Add security and optimization settings:
# HARDENING PHP allow_url_fopen = Off cgi.fix_pathinfo=0 expose_php = Off # OPTIMIZE PHP upload_max_filesize = 100M post_max_size = 125M max_input_vars = 3000 memory_limit = 256M
WordPress wp-config.php
Remember to also set memory limit in your WordPress wp-config.php file:
define('WP_MEMORY_LIMIT', '256M');
PHP-FPM Open File Limits
cd /etc/php/8.3/fpm/ sudo cp php-fpm.conf php-fpm.conf.bak sudo nano php-fpm.conf
Find and set:
rlimit_files = 32768 rlimit_core = unlimited
Apply changes:
sudo systemctl reload nginx sudo systemctl restart php8.3-fpm # Verify ps aux | grep php-fpm cat /proc/XXX/limits # Replace XXX with PID
🔧 MySQLTuner for Performance Analysis
Install and Run MySQLTuner
cd ~ mkdir MySQLTuner/ cd MySQLTuner/ wget http://mysqltuner.pl/ -O mysqltuner.pl chmod +x mysqltuner.pl # Run the tuner sudo ./mysqltuner.pl
MySQLTuner Benefits
MySQLTuner analyzes your MariaDB/MySQL installation and provides recommendations for:
- Memory usage optimization
- Query cache settings
- InnoDB configuration
- Security improvements
- Performance tuning
📊 Configuration Summary
| Component | Key Optimizations | Files Modified |
|---|---|---|
| NGINX | Worker processes, buffers, compression, file cache | nginx.conf + 6 include files |
| MariaDB | InnoDB buffer pool, binary logs, performance schema | 50-server.cnf |
| PHP-FPM | Memory limits, upload sizes, security hardening | server_override.ini, php-fpm.conf |
🎯 Best Practices
- Always backup configuration files before modifications
- Test NGINX configuration with
nginx -tbefore reloading - Monitor server performance after configuration changes
- Adjust buffer sizes based on actual traffic patterns
- Run MySQLTuner periodically to identify optimization opportunities
- Keep documentation of custom configurations
- Use bash aliases to streamline routine tasks
- Verify file limits match configured values
🔗 Reference Links
- NGINX Directive Index: http://nginx.org/en/docs/dirindex.html
- NGINX Documentation: Official documentation for detailed directive descriptions
- MySQLTuner: http://mysqltuner.pl/
📝 Final Checklist
| Task | Status |
|---|---|
| Backup original nginx.conf | ✓ |
| Create includes directory | ✓ |
| Configure main context | ✓ |
| Configure events context | ✓ |
| Create all include files | ✓ |
| Update nginx.conf with includes | ✓ |
| Test NGINX configuration | ✓ |
| Reload NGINX service | ✓ |
| Verify open file limits | ✓ |
| Configure MariaDB | ✓ |
| Configure PHP-FPM | ✓ |
| Set up bash aliases | ✓ |