Nginx server blocks (the equivalent of Apache virtual hosts) allow a single Nginx instance to serve multiple websites from the same IP address, distinguished by the server_name directive. Each block is an independent configuration defining the domain name, document root, SSL settings, URL rewrite rules, logging, and location-specific behaviour. On RHEL 9, server block files are stored in /etc/nginx/conf.d/ as individual .conf files, making it easy to add and manage dozens of sites without touching the main nginx.conf. This guide covers creating server blocks for HTTP and HTTPS sites, configuring try_files for clean URLs, implementing redirects, setting custom error pages, and testing the configuration.
Prerequisites
- Nginx installed and running on RHEL 9 (see: How to Install Nginx on RHEL 9)
- Domain names pointing to your server’s IP address (or use /etc/hosts for local testing)
Step 1 — Create the Document Roots
# Create document roots for two sites
mkdir -p /var/www/site1.com/html
mkdir -p /var/www/site2.com/html
# Set ownership and permissions
chown -R nginx:nginx /var/www/site1.com /var/www/site2.com
chmod -R 755 /var/www/site1.com /var/www/site2.com
# Create test pages
echo 'site1.com
' > /var/www/site1.com/html/index.html
echo 'site2.com
' > /var/www/site2.com/html/index.html
# Set SELinux context
semanage fcontext -a -t httpd_sys_content_t '/var/www/site1.com(/.*)?'
semanage fcontext -a -t httpd_sys_content_t '/var/www/site2.com(/.*)?'
restorecon -Rv /var/www/site1.com /var/www/site2.com
Step 2 — Create a Basic HTTP Server Block
vi /etc/nginx/conf.d/site1.com.conf
server {
listen 80;
listen [::]:80;
server_name site1.com www.site1.com;
root /var/www/site1.com/html;
index index.html index.php;
access_log /var/log/nginx/site1.com.access.log main;
error_log /var/log/nginx/site1.com.error.log warn;
# Clean URLs: try file, then directory, then 404
location / {
try_files $uri $uri/ =404;
}
# Deny access to hidden files (.htaccess, .git, etc.)
location ~ /. {
deny all;
access_log off;
log_not_found off;
}
}
Step 3 — Create an HTTPS Server Block with HTTP Redirect
vi /etc/nginx/conf.d/site2.com.conf
# HTTP block — redirect all HTTP traffic to HTTPS
server {
listen 80;
listen [::]:80;
server_name site2.com www.site2.com;
return 301 https://site2.com$request_uri;
}
# HTTPS block — main site configuration
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name site2.com www.site2.com;
root /var/www/site2.com/html;
index index.html;
ssl_certificate /etc/letsencrypt/live/site2.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site2.com/privkey.pem;
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_cache shared:SSL:10m;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
access_log /var/log/nginx/site2.com.access.log main;
error_log /var/log/nginx/site2.com.error.log warn;
location / {
try_files $uri $uri/ =404;
}
location ~ /. {
deny all;
}
}
Step 4 — Redirect www to non-www (or vice versa)
# Redirect www.example.com to example.com
server {
listen 80;
server_name www.example.com;
return 301 http://example.com$request_uri;
}
# Redirect non-www to www
server {
listen 80;
server_name example.com;
return 301 http://www.example.com$request_uri;
}
Step 5 — Common Location Block Patterns
# PHP-FPM integration
location ~ .php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Static asset caching
location ~* .(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# Custom 404 and 50x error pages
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
Step 6 — Test and Reload
# Test all configuration files for syntax errors
nginx -t
# Reload Nginx gracefully (zero downtime)
systemctl reload nginx
# Test that each site responds correctly
curl -H "Host: site1.com" http://localhost
curl -H "Host: site2.com" http://localhost
Step 7 — List and Manage Server Blocks
# List all server block files
ls -la /etc/nginx/conf.d/
# Temporarily disable a site by renaming
mv /etc/nginx/conf.d/site1.com.conf /etc/nginx/conf.d/site1.com.conf.disabled
nginx -t && systemctl reload nginx
# Re-enable
mv /etc/nginx/conf.d/site1.com.conf.disabled /etc/nginx/conf.d/site1.com.conf
nginx -t && systemctl reload nginx
Troubleshooting
- 403 Forbidden — check SELinux context (
ls -lZ /var/www/site1.com/html/) and that the web root has execute permission (chmod 755 /var/www/site1.com). - Multiple server_name blocks catching wrong site — add a
default_serverblock to catch unmatched requests:listen 80 default_server; - Config test passes but site still broken — check
/var/log/nginx/site1.com.error.logfor runtime errors.
Conclusion
Nginx server blocks on RHEL 9 are clean, self-contained .conf files in /etc/nginx/conf.d/. You can host unlimited sites from a single Nginx instance, each with its own document root, logging, SSL certificate, and location rules. Always run nginx -t before reloading to catch syntax errors.
Next steps: How to Secure Nginx with Let’s Encrypt on RHEL 9, How to Configure Nginx as a Reverse Proxy on RHEL 9, and How to Configure Apache Virtual Hosts on RHEL 9.