Nginx (pronounced “engine-x”) is the most widely deployed web server in the world, powering everything from small personal websites to the largest content delivery networks. On RHEL 9, Nginx is available from the AppStream repository and integrates natively with SELinux, firewalld, and systemd. Its event-driven, non-blocking architecture means a single Nginx worker process can handle thousands of concurrent connections while consuming far less memory than thread-per-connection alternatives like Apache. This guide covers installing Nginx from the RHEL 9 AppStream, configuring the firewall and SELinux for web traffic, testing the default installation, and setting up a basic server block to serve a website.

Prerequisites

  • RHEL 9 server with root or sudo access
  • A registered and subscribed RHEL 9 system (or AlmaLinux/Rocky Linux equivalent)
  • Port 80 and 443 accessible from the network

Step 1 — Install Nginx

dnf install -y nginx

The AppStream repository provides the current stable Nginx release. Verify the installed version:

nginx -v

Step 2 — Start and Enable Nginx

# Start Nginx and enable it to start at boot
systemctl enable --now nginx

# Verify it is running
systemctl status nginx

Step 3 — Open Firewall Ports

# Allow HTTP (port 80) and HTTPS (port 443)
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

# Verify
firewall-cmd --list-services

Step 4 — Verify the Default Page

Open a browser and navigate to your server’s IP address. You should see the RHEL 9 Nginx welcome page. Alternatively, test from the server:

curl -I http://localhost

Step 5 — Understand the Nginx Directory Structure

  • /etc/nginx/nginx.conf — the main configuration file
  • /etc/nginx/conf.d/ — drop-in server block configuration files
  • /usr/share/nginx/html/ — default web root
  • /var/log/nginx/access.log — access log
  • /var/log/nginx/error.log — error log

Step 6 — Configure the Main nginx.conf

vi /etc/nginx/nginx.conf
user nginx;
worker_processes auto;    # Automatically set to number of CPU cores
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;   # Max connections per worker
    use epoll;                 # Use efficient epoll event model
    multi_accept on;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;
    server_tokens       off;    # Hide Nginx version in headers

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    include /etc/nginx/conf.d/*.conf;
}

Step 7 — Create a Server Block for Your Site

# Create the web root
mkdir -p /var/www/example.com/html

# Set correct ownership
chown -R nginx:nginx /var/www/example.com/

# Create a test page
echo '

Welcome to example.com on RHEL 9

' > /var/www/example.com/html/index.html # Set SELinux context semanage fcontext -a -t httpd_sys_content_t '/var/www/example.com(/.*)?' restorecon -Rv /var/www/example.com/
vi /etc/nginx/conf.d/example.com.conf
server {
    listen       80;
    listen       [::]:80;
    server_name  example.com www.example.com;
    root         /var/www/example.com/html;
    index        index.html index.php;

    access_log  /var/log/nginx/example.com.access.log  main;
    error_log   /var/log/nginx/example.com.error.log   warn;

    location / {
        try_files $uri $uri/ =404;
    }
}
# Test configuration syntax
nginx -t

# Reload Nginx to apply changes
systemctl reload nginx

Step 8 — SELinux Considerations

RHEL 9 runs SELinux in enforcing mode by default. Several common Nginx tasks require SELinux booleans or context changes:

# Allow Nginx to connect to upstream backends (for reverse proxy)
setsebool -P httpd_can_network_connect 1

# Allow Nginx to connect to databases
setsebool -P httpd_can_network_connect_db 1

# Allow Nginx to serve files from non-standard directories
semanage fcontext -a -t httpd_sys_content_t '/custom/webroot(/.*)?'
restorecon -Rv /custom/webroot/

# Check what SELinux is blocking (if Nginx fails unexpectedly)
ausearch -c nginx -m avc --start today | audit2allow -a

Step 9 — Manage Nginx with systemd

systemctl start nginx
systemctl stop nginx
systemctl restart nginx     # Full restart (brief downtime)
systemctl reload nginx      # Graceful reload (zero downtime)
systemctl status nginx

Verification Checklist

# Nginx is running
systemctl is-active nginx

# Ports are open
firewall-cmd --list-services | grep -E 'http|https'

# Configuration is valid
nginx -t

# Test response
curl -si http://localhost | head -5

Conclusion

Nginx is installed and running on your RHEL 9 server with firewall and SELinux configured correctly. The server block in /etc/nginx/conf.d/ is serving your site, and the main configuration has sensible performance defaults including worker_processes auto and server_tokens off.

Next steps: How to Install Apache HTTP Server on RHEL 9, How to Configure Nginx Server Blocks on RHEL 9, and How to Secure Nginx with Let’s Encrypt on RHEL 9.