Install and Configure Nginx on RHEL 9 Step by Step
When a Linux administrator in Sydney needs a lean web stack to serve static content or reverse-proxy a Node.js backend, RHEL 9 with Nginx remains a dependable pairing that balances enterprise stability with modern features. The procedure is straightforward once you understand how Red Hat's package ecosystem, firewalld, and SELinux interact, and the same workflow works whether the box lives in a Melbourne colocation site or a Brisbane cloud region.
This walkthrough covers the practical commands and configuration choices used by Australian sysadmins, including repository setup, service management, server blocks, and the security tweaks that align with the Australian Cyber Security Centre's Essential Eight. Every step assumes a fresh RHEL 9 install with sudo access and a stable network connection.
Preparing the RHEL 9 Environment
Before touching any package manager, refresh the system so the AppStream and BaseOS modules are current. Run sudo dnf update -y and reboot if a kernel upgrade was applied, since older kernels occasionally cause instability with newer glibc versions on production hosts.
Register the system with Red Hat Subscription Manager if it is not already attached. Even on evaluation instances that you might provision for testing in Perth or Adelaide, an active subscription unlocks the nginx module stream. Verify the registration with sudo subscription-manager status and confirm the organisation name matches your business unit, which matters when multiple teams share an Australian managed-service provider.
SELinux ships in enforcing mode by default on RHEL 9, and that policy will block Nginx from connecting to upstream backends or writing to non-standard directories unless you either adjust the booleans or label files correctly. Leave enforcing on; the audit logs it produces in /var/log/audit/audit.log are a useful forensic trail that auditors often request under the Privacy Act 1988.
Installing Nginx from the Official Repository
RHEL 9 ships nginx 1.20 in the AppStream module stream, which is fine for most workloads but lags behind upstream releases. Install the default version with sudo dnf module install nginx:1.20 -y, which pulls in the main package plus the standard MIME types and helper utilities.
If you need a newer build, enable the official nginx repository from nginx.org. Create /etc/yum.repos.d/nginx.repo with the [nginx] stanza pointing at baseurl=https://nginx.org/packages/rhel/9/$basearch/, then run sudo dnf install nginx -y. Import the signing key first with sudo rpm --import https://nginx.org/keys/nginx_signing.key so dnf does not refuse the unsigned metadata. For deeper dives into module directives and HTTP/2 tuning, the Nginx web server guides cover edge cases that the man page glosses over.
After installation, confirm the binary version with nginx -v and check that the default configuration parses cleanly using sudo nginx -t. A clean parse here prevents awkward 403 pages later when SELinux denies access to a mislabelled document root.
Managing the Nginx Service and Firewall Rules
Enable the daemon so it survives reboots with sudo systemctl enable nginx, then start it using sudo systemctl start nginx. Verify it is listening on port 80 with sudo ss -tlnp | grep nginx, and confirm the worker process count with ps -ef | grep nginx. On a stock RHEL 9 image in a Sydney cloud region, the master process typically launches four workers by default, which is plenty for low-traffic internal portals.
RHEL's default firewall manager is firewalld, and it blocks inbound HTTP traffic until you add the service explicitly. Allow web traffic with sudo firewall-cmd --permanent --add-service=http and repeat for HTTPS using --add-service=https. Reload the firewall with sudo firewall-cmd --reload, then test from another machine with curl -I http://<server-ip>. If the response shows a 200 status with an nginx server header, the firewall and daemon are working together.
For hosts fronted by a corporate proxy or sitting behind a load balancer in a Melbourne data centre, you may also need to open the SELinux boolean httpd_can_network_connect so the proxy pass directive does not get blocked. Apply it with sudo setsebool -P httpd_can_network_connect 1.
Setting Up Server Blocks and SELinux
Server blocks are Nginx's equivalent of Apache virtual hosts and let one daemon serve multiple domains from a single IP, which is useful when a small business hosts both its marketing site on a .au domain and an internal staging environment. Create a directory for each site under /var/www/, for example /var/www/example.com.au/html, and assign ownership to the nginx user with sudo chown -R nginx:nginx /var/www/example.com.au.
Write a server block file at /etc/nginx/conf.d/example.com.au.conf with a server_name directive matching your domain and a root pointing at the document directory above. Restart the syntax test with sudo nginx -t before reloading with sudo systemctl reload nginx. Reloading keeps existing connections alive, which matters during business hours in Australian Eastern Standard Time when a hard restart would drop shopping-cart sessions.
Label every new document root with the httpd_sys_content_t type so SELinux permits reads. Use sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/example.com.au(/.*)?" followed by sudo restorecon -R /var/www/example.com.au. Skipping this step is the most common reason Australian admins see 403 Forbidden errors despite correct permissions and file ownership.
Hardening Nginx for Australian Compliance
The Australian Cyber Security Centre's Essential Eight asks organisations to harden internet-facing services, and Nginx is no exception. Disable server version disclosure by setting server_tokens off; in the http block, then add a strict ssl_protocols and ssl_ciphers directive that excludes TLS 1.0 and 1.1. The Notifiable Data Breaches scheme under the Privacy Act also expects you to log access attempts, so keep the default combined log format and ship it to your centralised SIEM.
If you handle personal information about Australian residents, consider geo-blocking at the firewall layer for countries where you have no customers, which reduces the attack surface for credential-stuffing campaigns that often originate overseas. Configure the limit_req_zone directive to throttle repeated requests on login endpoints, and pair it with fail2ban watching /var/log/nginx/access.log for the patterns your threat intel team flags.
Pair the web layer with a regular sudo dnf update nginx cadence, since Red Hat backports CVE fixes into the AppStream stream ahead of upstream's next minor release. Schedule the update window for Saturday afternoon AEST when your audience is unlikely to be browsing, and always test in a staging environment first.
Routine Checks After a Fresh Install
- Run
sudo nginx -tafter every configuration edit to catch syntax errors before reload. - Confirm
server_tokens off;is present and that HEAD requests return no version string. - Verify both
httpandhttpsservices are open in firewalld withsudo firewall-cmd --list-services. - Restore SELinux contexts on any custom document roots using
restorecon -Rv. - Rotate logs with
logrotateso/var/log/nginxdoes not fill/var. - Subscribe to Red Hat Security Advisories and the ACSC alert feed for nginx-related CVEs.
- Capture a baseline of
ss -tlnpoutput so you can spot rogue listeners during audits.
Run a quick smoke test against your staging hostname using curl -I https://staging.example.com.au and confirm the headers reflect your hardening choices. If anything fails during the walkthrough, contact the Linuxtpoint team and share the output of nginx -T alongside the last fifty lines of /var/log/audit/audit.log to speed up diagnosis. The concrete next step is provisioning a free Let's Encrypt certificate with sudo dnf install certbot python3-certbot-nginx -y followed by sudo certbot --nginx -d example.com.au -d www.example.com.au, which gives you a 90-day auto-renewing certificate trusted by every Australian browser.