How to Setup Nginx Reverse Proxy
An Nginx reverse proxy acts as a traffic cop between the internet and the web apps running on your server. It catches incoming requests on standard web ports like 80 and 443 and sends them to the right internal port.
Placing Nginx in front of an application built with Node.js or Python lets Nginx handle encrypted HTTPS connections and block bad traffic. It then passes the clean requests to your app on an internal port like 3000. Your app never has to face the internet directly.
Set up an Nginx reverse proxy by creating a server block in a file such as /etc/nginx/conf.d/myapp.conf, with a location block containing proxy_pass http://127.0.0.1:8080; to forward requests to your app. Then run sudo nginx -t to check the syntax and sudo systemctl reload nginx to apply it. Bind your app to 127.0.0.1 so it is reachable only through Nginx.
How to use Nginx as a reverse proxy server
Setting up an Nginx reverse proxy means routing incoming web traffic from the front-facing server to your backend application. You do this by creating a server block in your configuration files. The block tells Nginx the address and port where your app actually listens.
Before you write any configuration, check these requirements:
- Nginx must be installed. On Ubuntu or Debian, install it with
sudo apt install nginx. On RHEL-based systems such as Rocky Linux, AlmaLinux or Fedora, usesudo dnf install nginx. - You need root or sudo rights. The Nginx configuration files are owned by root, so you cannot edit them as a normal user.
- Know where the server block goes. On RHEL-based systems, create a file such as
/etc/nginx/conf.d/myapp.conf. On Ubuntu and Debian, you can do the same, or create the file in/etc/nginx/sites-available/and link it into/etc/nginx/sites-enabled/withsudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/. Nginx only reads the linked copy. - Open ports 80 and 443 in the firewall. On Ubuntu with UFW, run
sudo ufw allow 'Nginx Full'. On RHEL-based systems with firewalld, runsudo firewall-cmd --permanent --add-service=http --add-service=https, thensudo firewall-cmd --reload. - Allow SELinux network connections on RHEL-based systems. SELinux blocks Nginx from connecting to a backend by default. If you see 502 errors even though the app is running, run
sudo setsebool -P httpd_can_network_connect 1. The-Pmakes the change survive a reboot. Ubuntu does not use SELinux by default, so you can skip this there. - Bind the backend to 127.0.0.1 only. Configure your Node.js or Python app to listen on
127.0.0.1rather than0.0.0.0. Then the only way to reach it from outside is through Nginx. Do not open the app’s own port, such as 3000, in the firewall.
server {
listen 80;
server_name example.com;
location /backend_app {
proxy_pass http://127.0.0.1:8080;
}
}The configuration above tells Nginx to pass all requests to the /backend_app location to the proxied server at http://127.0.0.1:8080.
Nginx only reads its configuration when it starts or reloads, so the change does nothing until you test it and reload the service:
- Check the syntax by running
sudo nginx -t. - Look at the output. A good result shows syntax is ok and test is successful. If you see an error, it names the file and line number. Fix that line and run the test again.
- Reload Nginx with
sudo systemctl reload nginx. A reload applies the new configuration without dropping connections that are already open. If Nginx was not running yet, usesudo systemctl start nginxinstead. - Open your site in a browser, or request it with curl. You should now see the response from your backend app.
If something fails, check /var/log/nginx/error.log. A 502 Bad Gateway usually means the backend is not running, is listening on a different port, or is blocked by SELinux. To undo the change, remove the config file (or the symlink in sites-enabled), run the test again and reload.
Think of Nginx as a traffic director. It receives a request, like one for http://example.com/backend_app, and sends it to a specific backend server, such as one at http://127.0.0.1:8080. Nginx does this with the proxy_pass directive in its configuration file.
proxy_pass address.If it contains a path, even just a trailing slash, Nginx replaces the part of the request path that matched the location with it. If it has no path, Nginx passes the original request path through unchanged. Many "my app returns 404" problems come from this one character.
Nginx proxy servers handle more than plain web servers. Nginx can proxy requests to FastCGI, uwsgi and SCGI application servers. Through the stream module, it can also proxy raw TCP and UDP services. Stream proxying is configured in a separate stream block rather than inside http. Run nginx -V to check that your build includes the module. Some distributions ship it as a separate package.
- fastcgi_pass - reverse proxy to a FastCGI server.
- uwsgi_pass - reverse proxy to a uwsgi server.
- scgi_pass - reverse proxy to an SCGI server.
A common Nginx reverse proxy to non-HTTP hosts is done using PHP-FPM. An example is how Nginx serves PHP scripts: Nginx receives the request and hands it to a PHP process manager, which runs the script and returns the output.
Nginx reverse proxy settings manage web requests reaching backend applications. The proxy_pass setting directs traffic to specific servers. These settings work for most setups, but some apps need extra tuning.
Most apps want to know the original host name and client address. Nginx does not forward these on its own, so people add proxy_set_header Host $host;, proxy_set_header X-Real-IP $remote_addr;, proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; and proxy_set_header X-Forwarded-Proto $scheme; inside the location block. Without them, your app sees every visitor as 127.0.0.1.
The second example below is meant for apps that use WebSockets, such as chat tools, live dashboards and many Node.js frameworks. A WebSocket starts as a normal HTTP request that asks to switch protocols. Nginx drops that request by default, so you have to forward it yourself. This is what the Upgrade and Connection headers are for. You also need proxy_http_version 1.1;, because the default version, HTTP/1.0, cannot do the upgrade.
Check two things in that example before you copy it:
- The location line must have a space between the word and the path. Write
location / {, notlocation/ {. Nginx will not accept the version without the space, andnginx -twill report an error. - Setting
Connection "upgrade"unconditionally tells the backend to upgrade every request, including ordinary ones. A cleaner way is amapblock in thehttpcontext, which any file inconf.dalready is:map $http_upgrade $connection_upgrade { default upgrade; '' close; }. Then useproxy_set_header Upgrade $http_upgrade;andproxy_set_header Connection $connection_upgrade;in the location block. Ordinary requests then get close, and WebSocket requests get upgrade.
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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 X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}Two additions come up on almost every real server: HTTPS and load balancing.
HTTPS with Let's Encrypt and Certbot. Nginx can end the encrypted connection and talk plain HTTP to your app on 127.0.0.1. Your domain's DNS record must already point at the server, and port 80 must be reachable from the internet, because Let's Encrypt checks your domain over it.
- Install Certbot and its Nginx plugin. On Ubuntu or Debian, run
sudo apt install certbot python3-certbot-nginx. On RHEL-based systems, the package names and source differ, so follow the Certbot instructions for your distribution. - Request a certificate with
sudo certbot --nginx -d example.com, using your own domain. - Answer the prompts. Certbot asks for an email address and whether you accept the terms. It also asks whether to redirect HTTP to HTTPS. Choose the redirect.
- Certbot edits your server block for you. When it finishes, the block listens on
listen 443 ssl;and pointsssl_certificateat/etc/letsencrypt/live/example.com/fullchain.pemandssl_certificate_keyat/etc/letsencrypt/live/example.com/privkey.pem. Yourproxy_passline stays inside it. - Test and reload with
sudo nginx -tandsudo systemctl reload nginx. - Check that renewal works with
sudo certbot renew --dry-run. Certificates last 90 days, and Certbot normally renews them automatically through a scheduled job or timer.
If you write the HTTPS block by hand, keep a separate server block for port 80 that only redirects. It needs listen 80;, the same server_name, and return 301 https://$host$request_uri;. Every plain HTTP request is then sent to the HTTPS address.
Load balancing with upstream. If you run more than one copy of your app, define them as a group and proxy to the group instead of to a single address:
- Outside any server block, in the
httpcontext, addupstream backend_pool { server 127.0.0.1:3000; server 127.0.0.1:3001; }. Put oneserverline in it for each app instance. The name backend_pool is just an example. - In your location block, change the target to the group name with
proxy_pass http://backend_pool;. - Test and reload with
sudo nginx -tandsudo systemctl reload nginx.
By default Nginx shares requests across the servers in turn (round robin). You can add least_conn; inside the upstream block to favor the server with the fewest active connections, or add weight= to a server line to send it more traffic. If a server stops responding, Nginx skips it for a while.
For more advanced configurations or options suited to your specific environment, the official Nginx website is an excellent resource. This covers the basics of setting up Nginx as a reverse proxy.
Conclusion:
Configuring Nginx as a reverse proxy noticeably boosts the efficiency and performance of web applications. It also keeps your backend off the public network.
- Flexibility: Nginx can handle both HTTP and non-HTTP requests, making it suitable for various backend applications.
- Efficiency: It offloads the processing from backend servers, managing tasks like SSL encryption and caching.
- Scalability: Load balancing features help distribute client requests efficiently across multiple servers, improving performance.
- Customizability: Nginx's configuration options allow you to tailor the reverse proxy settings to meet your specific needs.
- Support for Modern Applications: Use Nginx with popular backend technologies like Node.js, Python, and PHP.
By following the basic configurations and using the options discussed, you can effectively implement Nginx as a reverse proxy in your environment. Remember the routine for every change: edit the config, run sudo nginx -t, then reload Nginx.
Have a question about this guide?
Answered by AI from this guide. Good questions may be added to this page — please don't include personal details.
Did this guide fix your problem?
About the Author
Richard
Tech Writer, IT Professional
Richard is a writer at Geek Rewind who turns complex IT tasks into clear, step-by-step guides. He draws on years of hands-on experience in system administration and enterprise IT operations, focusing on Windows, Linux and WordPress: the problems people actually run into, and the fixes that work. His server and WordPress guides come from systems he runs himself. Richard builds and maintains the platform behind Geek Rewind, from its Ubuntu servers and Nginx configuration to its custom WordPress plugins. Many new tutorials start with readers' questions, and he's always glad to answer them in the comments.
No comments yet — be the first to share your thoughts!