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_server block to catch unmatched requests: listen 80 default_server;
  • Config test passes but site still broken — check /var/log/nginx/site1.com.error.log for 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.