Skip to Content
DocsServerNginx & Apache

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 -v

Firewall Configuration

# Allow HTTP and HTTPS sudo ufw allow 'Nginx Full' # Or allow individually sudo ufw allow 80/tcp sudo ufw allow 443/tcp

Directory 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 directory

Virtual 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 nginx

Reverse 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 nginx

Nginx 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 nginx

Apache

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 -v

Firewall 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 directory

Common 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 apache2

Virtual 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 apache2

Reverse 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 apache2

SSL 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 -y

Nginx 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 certificates

Certbot 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.com

Manual 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; EOF

Automatic 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 certbot

Nginx vs Apache: Choosing the Right One

ScenarioRecommendationReason
High-concurrency static filesNginxEvent-driven, low memory usage
.htaccess requirementsApacheNative support for directory-level configuration
Reverse proxy / load balancingNginxClean configuration, excellent performance
PHP (PHP-FPM)EitherBoth support FastCGI; recommended on 26.04
Microservices gatewayNginxBetter 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 log
Last updated on