A reverse proxy sits in front of one or more backend application servers, forwarding client requests to them and returning their responses to the client. From the client’s perspective, the reverse proxy is the web server. This architecture provides centralized SSL termination, load balancing, caching, rate limiting, and DDoS mitigation without exposing backend application ports to the internet. Nginx is an excellent reverse proxy: its event-driven architecture handles thousands of concurrent connections while forwarding requests to Node.js, Python (Gunicorn/uWSGI), Java, Go, PHP-FPM, or any other HTTP backend. This guide covers basic HTTP and HTTPS reverse proxying with Nginx on RHEL 9, including correct header passing, WebSocket proxying, upstream health checks, and buffer tuning.
Prerequisites
- Nginx installed on RHEL 9
- A backend application listening on a local port (e.g., a Node.js app on port 3000)
Step 1 — Basic Reverse Proxy Configuration
vi /etc/nginx/conf.d/myapp.conf
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Pass original client headers to 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;
# Disable buffering for SSE or streaming responses
proxy_buffering off;
proxy_read_timeout 90s;
}
}
nginx -t && systemctl reload nginx
# Allow Nginx to make network connections (SELinux)
setsebool -P httpd_can_network_connect 1
Step 2 — Reverse Proxy with SSL Termination
server {
listen 80;
server_name app.example.com;
return 301 https://app.example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://127.0.0.1:3000;
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_read_timeout 90s;
}
}
Step 3 — Upstream Block for Multiple Backends
# Define an upstream group for load balancing
upstream myapp_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
keepalive 32; # Keep connections to backends warm
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://myapp_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Step 4 — WebSocket Proxying
WebSocket connections require an Upgrade header. Add these directives to proxy WebSocket traffic correctly:
location /ws/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s; # Keep WebSocket connections alive
}
Step 5 — Proxy Specific Paths to Different Backends
server {
listen 80;
server_name app.example.com;
# API routes to a Node.js backend
location /api/ {
proxy_pass http://127.0.0.1:4000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Static assets served directly by Nginx
location /static/ {
alias /var/www/myapp/static/;
expires 30d;
}
# Everything else to the main app
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Step 6 — Buffer and Timeout Tuning
# Add inside the server or location block
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Buffer settings for large responses
proxy_buffers 8 16k;
proxy_buffer_size 32k;
proxy_busy_buffers_size 64k;
Verification Checklist
nginx -t
curl -I http://app.example.com
curl -si http://app.example.com | grep X-Forwarded
journalctl -u nginx -f
Conclusion
Nginx reverse proxy on RHEL 9 forwards requests to your backend applications while handling SSL termination, header injection, WebSocket upgrade, and path-based routing. The upstream block enables load balancing across multiple backend instances with keepalive connection pooling for maximum throughput.
Next steps: How to Configure Apache mod_proxy as a Reverse Proxy on RHEL 9, How to Configure Nginx Load Balancing on RHEL 9, and How to Configure Nginx Rate Limiting and Connection Throttling on RHEL 9.