Nginx / Apache Web Servers
This article covers installing and configuring the two most popular web servers on Ubuntu 26.04: Nginx and Apache.
Nginx
Installing Nginx
sudo apt update
sudo apt install nginx -y
# Start and enable on boot
sudo systemctl enable --now nginx
# Check status
sudo systemctl status nginx
# Check version
nginx -vFirewall Configuration
# Allow HTTP and HTTPS
sudo ufw allow 'Nginx Full'
# Or allow individually
sudo ufw allow 80/tcp
sudo ufw allow 443/tcpDirectory Structure
/etc/nginx/
├── nginx.conf # Main configuration file
├── sites-available/ # Available site configurations
├── sites-enabled/ # Enabled sites (symlinks)
├── conf.d/ # Additional configuration snippets
├── snippets/ # Reusable configuration snippets
└── modules-enabled/ # Enabled modules
/var/www/ # Website root directory
/var/log/nginx/ # Log directoryVirtual Host Configuration
# Create website directory
sudo mkdir -p /var/www/example.com/html
sudo chown -R www-data:www-data /var/www/example.com
echo "<h1>Welcome to example.com</h1>" | sudo tee /var/www/example.com/html/index.html
# Create virtual host configuration
sudo tee /etc/nginx/sites-available/example.com << 'EOF'
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html index.htm;
# Logs
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
location / {
try_files $uri $uri/ =404;
}
# Static asset caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Deny access to hidden files
location ~ /\. {
deny all;
}
}
EOF
# Enable the site
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# Remove default site (optional)
sudo rm /etc/nginx/sites-enabled/default
# Test configuration
sudo nginx -t
# Reload
sudo systemctl reload nginxReverse Proxy Configuration
sudo tee /etc/nginx/sites-available/app-proxy << 'EOF'
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
EOF
sudo ln -s /etc/nginx/sites-available/app-proxy /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginxNginx Performance Tuning
# /etc/nginx/nginx.conf key settings
sudo tee /etc/nginx/conf.d/performance.conf << 'EOF'
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml
application/rss+xml
image/svg+xml;
# File caching
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
# Connection optimization
keepalive_timeout 65;
keepalive_requests 100;
EOF
sudo nginx -t && sudo systemctl reload nginxApache
Installing Apache
sudo apt update
sudo apt install apache2 -y
# Start and enable on boot
sudo systemctl enable --now apache2
# Check status
sudo systemctl status apache2
# Check version
apache2 -vFirewall Configuration
sudo ufw allow 'Apache Full'Directory Structure
/etc/apache2/
├── apache2.conf # Main configuration file
├── ports.conf # Listening ports
├── sites-available/ # Available site configurations
├── sites-enabled/ # Enabled sites
├── mods-available/ # Available modules
├── mods-enabled/ # Enabled modules
└── conf-available/ # Additional configuration
/var/www/ # Website root directory
/var/log/apache2/ # Log directoryCommon Module Management
# Enable modules
sudo a2enmod rewrite # URL rewriting
sudo a2enmod ssl # SSL/TLS
sudo a2enmod headers # HTTP header control
sudo a2enmod proxy # Reverse proxy
sudo a2enmod proxy_http # HTTP proxy
sudo a2enmod proxy_wstunnel # WebSocket proxy
# Disable a module
sudo a2dismod autoindex
# Restart to apply changes
sudo systemctl restart apache2Virtual Host Configuration
# Create website directory
sudo mkdir -p /var/www/example.com/html
sudo chown -R www-data:www-data /var/www/example.com
echo "<h1>Welcome to example.com</h1>" | sudo tee /var/www/example.com/html/index.html
# Create virtual host configuration
sudo tee /etc/apache2/sites-available/example.com.conf << 'EOF'
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
ServerAdmin admin@example.com
DocumentRoot /var/www/example.com/html
<Directory /var/www/example.com/html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Logs
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
EOF
# Enable the site
sudo a2ensite example.com.conf
# Disable the default site (optional)
sudo a2dissite 000-default.conf
# Test configuration
sudo apache2ctl configtest
# Reload
sudo systemctl reload apache2Reverse Proxy Configuration
sudo a2enmod proxy proxy_http
sudo tee /etc/apache2/sites-available/app-proxy.conf << 'EOF'
<VirtualHost *:80>
ServerName app.example.com
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
# WebSocket proxy
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) ws://127.0.0.1:3000/$1 [P,L]
ErrorLog ${APACHE_LOG_DIR}/app-proxy-error.log
CustomLog ${APACHE_LOG_DIR}/app-proxy-access.log combined
</VirtualHost>
EOF
sudo a2ensite app-proxy.conf
sudo apache2ctl configtest && sudo systemctl reload apache2SSL Certificate Configuration
Using Let’s Encrypt (Certbot)
# Install Certbot
sudo apt install certbot -y
# Nginx plugin
sudo apt install python3-certbot-nginx -y
# Apache plugin
sudo apt install python3-certbot-apache -yNginx SSL Configuration
# Automatically obtain and configure certificates
sudo certbot --nginx -d example.com -d www.example.com
# Test automatic renewal
sudo certbot renew --dry-run
# View certificate information
sudo certbot certificatesCertbot will automatically modify the Nginx configuration to:
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# ... other configuration
}
# HTTP to HTTPS redirect
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}Apache SSL Configuration
# Enable the SSL module
sudo a2enmod ssl
# Automatically obtain and configure certificates
sudo certbot --apache -d example.com -d www.example.comManual SSL Configuration (Nginx)
sudo tee /etc/nginx/snippets/ssl-params.conf << 'EOF'
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 223.5.5.5 8.8.8.8 valid=300s;
EOFAutomatic Renewal
# Certbot automatically configures a systemd timer during installation
sudo systemctl status certbot.timer
# Manual renewal
sudo certbot renew
# View the renewal schedule
sudo systemctl list-timers | grep certbotNginx vs Apache: Choosing the Right One
| Scenario | Recommendation | Reason |
|---|---|---|
| High-concurrency static files | Nginx | Event-driven, low memory usage |
| .htaccess requirements | Apache | Native support for directory-level configuration |
| Reverse proxy / load balancing | Nginx | Clean configuration, excellent performance |
| PHP (PHP-FPM) | Either | Both support FastCGI; recommended on 26.04 |
| Microservices gateway | Nginx | Better suited for API gateway scenarios |
Warning
On Ubuntu 26.04, the Apache 2.4.66 service enables MemoryDenyWriteExecute=yes by default, which causes libapache2-mod-php to crash. The official recommendation is to use PHP-FPM (mod_proxy_fcgi + php-fpm) instead of the embedded mod_php.
Common Management Commands
# Nginx
sudo nginx -t # Test configuration
sudo systemctl reload nginx # Graceful reload
sudo systemctl restart nginx # Restart
sudo tail -f /var/log/nginx/access.log # View access log
# Apache
sudo apache2ctl configtest # Test configuration
sudo systemctl reload apache2 # Graceful reload
sudo systemctl restart apache2 # Restart
sudo a2ensite site.conf # Enable site
sudo a2dissite site.conf # Disable site
sudo a2enmod module # Enable module
sudo a2dismod module # Disable module
sudo tail -f /var/log/apache2/access.log # View access logRelated Articles
- Docker Compose Service Orchestration — Deploy web services using Docker Compose containers
- SSH Basics — SSH remote connections and key management, essential skills for server administration
- UFW Firewall — Configure firewall rules for web servers, open HTTP/HTTPS ports
Last updated on