How to Configure Nginx as a Reverse Proxy on RHEL 7
A reverse proxy sits in front of one or more backend application servers, forwarding incoming client requests and returning the backend responses. Nginx is exceptionally well-suited for this role due to its event-driven architecture and low memory footprint. On RHEL 7, a common pattern is to run a Node.js, Python, or Java application on a high-numbered port (such as 3000) and place Nginx on port 80 or 443 to handle SSL termination, load distribution, caching, and header manipulation. This tutorial covers the complete setup of Nginx as a reverse proxy on RHEL 7, including the critical SELinux configuration that trips up many administrators.
Prerequisites
- RHEL 7 server with root or sudo access
- A backend application listening on a local port (this guide uses port 3000 as an example)
- Nginx installed (covered in Step 1)
- Basic familiarity with Nginx configuration syntax
Step 1: Install Nginx from EPEL
Nginx is available in the EPEL repository for RHEL 7. First enable EPEL if you have not already done so:
sudo yum install -y epel-release
sudo yum install -y nginx
Enable and start the Nginx service:
sudo systemctl enable nginx
sudo systemctl start nginx
sudo systemctl status nginx
Open the required firewall ports:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Step 2: Understand the Nginx Configuration Layout on RHEL 7
On RHEL 7, the main Nginx configuration file is /etc/nginx/nginx.conf. The http block inside that file includes any files matching /etc/nginx/conf.d/*.conf. Best practice is to create a separate file under conf.d/ for each site or application rather than editing the main file directly.
ls /etc/nginx/conf.d/
# default.conf (default server block)
Step 3: Configure an Upstream Block
The upstream directive defines a named group of backend servers. Although a single-server proxy can use proxy_pass with a direct address, using an upstream block is better practice because it makes future changes (adding more backends, adjusting weights) easy without touching the location block. Create a new configuration file:
sudo nano /etc/nginx/conf.d/nodeapp.conf
Add an upstream block at the top:
upstream nodeapp_backend {
server 127.0.0.1:3000;
keepalive 32;
}
The keepalive 32 directive keeps up to 32 idle connections open to the backend, reducing the overhead of repeatedly opening TCP connections for each request.
Step 4: Configure the Server Block with proxy_pass
Below the upstream block in the same file, add the server block:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
access_log /var/log/nginx/nodeapp_access.log;
error_log /var/log/nginx/nodeapp_error.log;
location / {
proxy_pass http://nodeapp_backend;
proxy_http_version 1.1;
# Pass the real client IP to the backend
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;
# Required for HTTP keepalive to the backend
proxy_set_header Connection "";
}
}
The four proxy_set_header directives are essential. Without them, your backend application sees all requests arriving from 127.0.0.1 rather than the real client IP, which breaks logging, rate limiting, and geolocation features.
Step 5: Tune Proxy Buffering Settings
Nginx can buffer backend responses before sending them to clients. For most applications this improves throughput, but it can introduce latency for streaming or real-time responses. Add buffering directives inside the location block:
location / {
proxy_pass http://nodeapp_backend;
proxy_http_version 1.1;
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;
proxy_set_header Connection "";
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Pass errors from backend to client
proxy_intercept_errors off;
}
For WebSocket or Server-Sent Events applications, disable buffering and add the Upgrade header:
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Step 6: Configure SELinux to Allow Network Connections
This step is the most common source of errors on RHEL 7. SELinux is enforcing by default and blocks Nginx from making outbound network connections to backend ports. You will see errors like connect() to 127.0.0.1:3000 failed (13: Permission denied) in /var/log/nginx/error.log.
Enable the httpd_can_network_connect boolean to allow Nginx (and Apache) to connect to any network port:
# Enable permanently (survives reboot)
sudo setsebool -P httpd_can_network_connect 1
# Verify
getsebool httpd_can_network_connect
If your backend runs on a non-standard port, you can alternatively label just that port instead of opening all network connections:
sudo semanage port -a -t http_port_t -p tcp 3000
sudo semanage port -l | grep http_port_t
The semanage command requires the policycoreutils-python package:
sudo yum install -y policycoreutils-python
Step 7: Test and Reload Nginx
Test the configuration syntax:
sudo nginx -t
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Reload to apply changes without dropping existing connections:
sudo systemctl reload nginx
Step 8: Verify Proxy Headers Are Being Forwarded
Start a simple test server on port 3000 that prints all request headers (requires Node.js or Python):
# Python 3 one-liner that prints headers
python3 -m http.server 3000 &
# Or with Node.js
node -e "require('http').createServer((req,res)=>{
console.log(req.headers); res.end('OK');
}).listen(3000);" &
Send a request through Nginx:
curl -I http://yourdomain.com/
Check that the backend receives x-real-ip, x-forwarded-for, and host headers matching your client IP and domain name.
Step 9: Add a Health Check Location (Optional)
Nginx open-source does not include active health checks (that feature is in Nginx Plus), but you can expose the built-in stub status page to monitor Nginx itself:
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
Access it locally:
curl http://127.0.0.1/nginx_status
This returns active connections, accepted requests, and handled connections counts.
Configuring Nginx as a reverse proxy on RHEL 7 involves four key elements: an upstream block naming the backend, a server block with proxy_pass pointing at that upstream, correct proxy headers to preserve client identity, and the SELinux httpd_can_network_connect boolean to permit outbound connections. Once these pieces are in place you gain a robust, high-performance gateway in front of your application server that can later be extended with SSL termination, caching, rate limiting, and load balancing with minimal additional configuration.