{"id":119355,"date":"2026-06-29T17:04:51","date_gmt":"2026-06-29T11:34:51","guid":{"rendered":"https:\/\/www.guvi.in\/blog\/?p=119355"},"modified":"2026-06-29T17:04:53","modified_gmt":"2026-06-29T11:34:53","slug":"nginx-reverse-proxy-tutorial-step-by-step-guide","status":"publish","type":"post","link":"https:\/\/www.guvi.in\/blog\/nginx-reverse-proxy-tutorial-step-by-step-guide\/","title":{"rendered":"NGINX Reverse Proxy Tutorial: Step-by-Step Guide"},"content":{"rendered":"\n<p>Most modern websites don&#8217;t expose their backend servers directly to the internet. Instead, they use a reverse proxy to handle incoming traffic, improve security, and manage requests efficiently.<\/p>\n\n\n\n<p>NGINX is one of the most popular tools for this job, but a small configuration mistake can quickly lead to frustrating errors like 502 Bad Gateway.&nbsp;<\/p>\n\n\n\n<p>This tutorial guides you through setting up an NGINX reverse proxy from scratch, explains what each configuration does, and shows you how to troubleshoot common issues with confidence.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>TL;DR Summary<\/strong><\/h2>\n\n\n\n<ul>\n<li>An NGINX reverse proxy sits between clients and your backend servers, forwarding requests and returning responses.<\/li>\n\n\n\n<li>It&#8217;s used for load balancing, SSL termination, caching, and hiding internal server architecture.<\/li>\n\n\n\n<li>Setup takes about 15\u201320 minutes if you already have NGINX installed and a backend app running.<\/li>\n\n\n\n<li>This guide walks through installation, configuration, SSL setup, and common troubleshooting steps.<\/li>\n\n\n\n<li>You&#8217;ll finish with a working proxy you can adapt for production.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<div style=\"background-color: #099f4e; border: 3px solid #110053; border-radius: 12px; padding: 18px 22px; color: #FFFFFF; font-size: 18px; font-family: Montserrat, Helvetica, sans-serif; line-height: 1.6; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); max-width: 750px;\">\n\n<strong style=\"font-size: 22px; color: #ffffff;\">\ud83d\udca1 Did You Know?<\/strong> <br \/><br \/>\n\n  <span>\n\n\n<strong style=\"color: #110053;\">NGINX<\/strong> powers about <strong style=\"color: #110053;\">33%<\/strong> of all identifiable web servers globally, making it the world&#8217;s most widely used web server software.\n\n\n  <\/span>\n\n<\/div>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What Is an NGINX Reverse Proxy?<\/strong><\/h2>\n\n\n\n<p>An NGINX reverse proxy is a server configuration in which <a href=\"https:\/\/en.wikipedia.org\/wiki\/Nginx\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">NGINX<\/a> receives client requests, forwards them to one or more backend servers, and returns the response to the client.\u00a0<\/p>\n\n\n\n<p>In simple terms, a reverse proxy is a server that sits in front of one or more backend servers. It intercepts client requests and decides where to send them.<\/p>\n\n\n\n<p>NGINX, when configured as a reverse proxy, accepts incoming <a href=\"https:\/\/www.guvi.in\/blog\/http-in-computer-networks\/\" target=\"_blank\" rel=\"noreferrer noopener\">HTTP<\/a>\/HTTPS requests on behalf of your backend application (like a <a href=\"https:\/\/www.guvi.in\/blog\/guide-for-nodejs-as-backend\/\" target=\"_blank\" rel=\"noreferrer noopener\">Node.js<\/a>, Python, or Java server).\u00a0<\/p>\n\n\n\n<p>It then forwards the request internally, waits for a response, and sends that response back to the client. The client never talks directly to your app server \u2014 it only ever sees NGINX. This is why reverse proxies are a core piece of almost every production web architecture today.<\/p>\n\n\n\n<p><strong>Also Read: <\/strong><a href=\"https:\/\/www.guvi.in\/blog\/a-detailed-guide-on-proxy-and-its-types\/\" target=\"_blank\" rel=\"noreferrer noopener\"><strong><em>Proxy: A Detailed Guide on its Overview and Types<\/em><\/strong><\/a><\/p>\n\n\n\n<p><em>Build job-ready NGINX skills with <\/em><strong><em>HCL GUVI&#8217;s <\/em><\/strong><a href=\"https:\/\/www.guvi.in\/courses\/web-development\/nginx\/?utm_source=blog&amp;utm_medium=hyperlink&amp;utm_campaign=nginx-reverse-proxy-tutorial\" target=\"_blank\" rel=\"noreferrer noopener\"><strong><em>NGINX Certification Course<\/em><\/strong><\/a><em>. Learn to configure NGINX, implement reverse proxies and load balancers, secure websites with HTTPS, optimize performance, and monitor applications with confidence. Enroll today and gain hands-on experience with one of the most widely used web servers!<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Why Use a Reverse Proxy?<\/strong><\/h2>\n\n\n\n<p>A reverse proxy isn&#8217;t just a nice-to-have \u2014 it solves real operational problems.<\/p>\n\n\n\n<ul>\n<li><strong>Security:<\/strong> Hides your backend&#8217;s IP address, ports, and stack details.<\/li>\n\n\n\n<li><strong>Load balancing:<\/strong> Distributes traffic across multiple backend instances.<\/li>\n\n\n\n<li><strong>SSL termination:<\/strong> Handles HTTPS encryption\/decryption so your app doesn&#8217;t have to.<\/li>\n\n\n\n<li><strong>Caching:<\/strong> Serves repeated requests faster without hitting the backend.<\/li>\n\n\n\n<li><strong>Single entry point:<\/strong> Lets you run several apps on one server using different paths or subdomains.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Prerequisites Before Setting Up NGINX Reverse Proxy&nbsp;<\/strong><\/h2>\n\n\n\n<p>Before starting, make sure you have:<\/p>\n\n\n\n<ul>\n<li>A Linux server (Ubuntu 22.04\/24.04 examples used here)<\/li>\n\n\n\n<li>Root or sudo access<\/li>\n\n\n\n<li>A backend application running on a local port (e.g., localhost:3000)<\/li>\n\n\n\n<li>A domain name pointed to your server&#8217;s IP (optional, but needed for SSL)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step-by-Step Guide to Configure NGINX Reverse Proxy&nbsp;<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 1: Install NGINX<\/strong><\/h3>\n\n\n\n<p>sudo apt update<\/p>\n\n\n\n<p>sudo apt install nginx -y<\/p>\n\n\n\n<p>sudo systemctl enable nginx<\/p>\n\n\n\n<p>sudo systemctl start nginx<\/p>\n\n\n\n<p>Verify it&#8217;s running:<\/p>\n\n\n\n<p>sudo systemctl status nginx<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 2: Confirm Your Backend App Is Running<\/strong><\/h3>\n\n\n\n<p>For this example, assume an app is running on port 3000:<\/p>\n\n\n\n<p>curl http:\/\/localhost:3000<\/p>\n\n\n\n<p>You should get a response from your app. If not, fix that first \u2014 NGINX can&#8217;t proxy to a service that isn&#8217;t running.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 3: Create a New Server Block Config<\/strong><\/h3>\n\n\n\n<p>Create a new config file instead of editing the default one:<\/p>\n\n\n\n<p>sudo nano \/etc\/nginx\/sites-available\/myapp<\/p>\n\n\n\n<p>Add this configuration:<\/p>\n\n\n\n<p>server {<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;listen 80;<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;server_name example.com www.example.com;<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;location \/ {<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proxy_pass http:\/\/127.0.0.1:3000;<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proxy_set_header Host $host;<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proxy_set_header X-Real-IP $remote_addr;<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proxy_set_header X-Forwarded-Proto $scheme;<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;}<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<ul>\n<li>The proxy_pass directive tells NGINX where to send the request.&nbsp;<\/li>\n\n\n\n<li>The proxy_set_header lines preserve important information (the original host, client IP, and protocol) that would otherwise be lost since the backend only sees NGINX&#8217;s internal IP.&nbsp;<\/li>\n\n\n\n<li>Without these headers, your app&#8217;s logs and security checks will show the wrong client IP for every request.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 4: Enable the Site<\/strong><\/h3>\n\n\n\n<p>sudo ln -s \/etc\/nginx\/sites-available\/myapp \/etc\/nginx\/sites-enabled\/<\/p>\n\n\n\n<p>sudo nginx -t<\/p>\n\n\n\n<p>sudo systemctl reload nginx<\/p>\n\n\n\n<p>\u26a0\ufe0f <strong>Warning:<\/strong> Always run nginx -t before reloading. It catches syntax errors before they take down your live site.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Step 5: Test It<\/strong><\/h3>\n\n\n\n<p>Visit your domain or run:<\/p>\n\n\n\n<p>curl -H &#8220;Host: example.com&#8221; http:\/\/localhost<\/p>\n\n\n\n<p>If you see your app&#8217;s response, the proxy is working.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Adding SSL\/TLS to Your Reverse Proxy<\/strong><\/h2>\n\n\n\n<p>Running HTTP-only in 2026 is a non-starter for most real traffic. Use Certbot for free, automated certificates:<\/p>\n\n\n\n<p>sudo apt install certbot python3-certbot-nginx -y<\/p>\n\n\n\n<p>sudo certbot &#8211;nginx -d example.com -d www.example.com<\/p>\n\n\n\n<p>Certbot edits your NGINX config automatically, adding a listen 443 ssl block and redirecting HTTP to HTTPS.<\/p>\n\n\n\n<p>\u2705 <strong>Best Practice:<\/strong> Set up auto-renewal with sudo certbot renew &#8211;dry-run to confirm it&#8217;ll renew without manual intervention.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Reverse Proxy vs. Forward Proxy vs. Load Balancer<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>Feature<\/strong><\/td><td><strong>Reverse Proxy<\/strong><\/td><td><strong>Forward Proxy<\/strong><\/td><td><strong>Load Balancer<\/strong><\/td><\/tr><tr><td><strong>Sits in front of<\/strong><\/td><td>Servers<\/td><td>Clients<\/td><td>Multiple servers<\/td><\/tr><tr><td><strong>Main Purpose<\/strong><\/td><td>Protect\/manage backend<\/td><td>Anonymize\/filter client traffic<\/td><td>Distribute traffic<\/td><\/tr><tr><td><strong>Common Tool<\/strong><\/td><td>NGINX, HAProxy<\/td><td>Squid<\/td><td>NGINX, HAProxy, AWS ELB<\/td><\/tr><tr><td><strong>Client Awareness<\/strong><\/td><td>The client is unaware of the backend<\/td><td>The server is unaware of the real client<\/td><td>The client is unaware of which server responds<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>In practice, NGINX often does all three jobs at once \u2014 which is part of why it&#8217;s so widely adopted.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Common Errors and How to Fix Them<\/strong><\/h2>\n\n\n\n<ul>\n<li><strong>502 Bad Gateway:<\/strong> Usually means your backend app isn&#8217;t running or is on the wrong port. Check with curl localhost:PORT directly.<\/li>\n\n\n\n<li><strong>413 Request Entity Too Large:<\/strong> Add client_max_body_size 20M; inside your server block.<\/li>\n\n\n\n<li><strong>Connection refused:<\/strong> Confirm your backend is bound to 127.0.0.1 and not blocked by a firewall rule.<\/li>\n\n\n\n<li><strong>Mixed content warnings after SSL:<\/strong> Make sure proxy_set_header X-Forwarded-Proto $scheme; is present so your app knows it&#8217;s behind HTTPS.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What to Do Next<\/strong><\/h2>\n\n\n\n<p>Before wrapping up, here&#8217;s how to keep moving:<\/p>\n\n\n\n<ol>\n<li>Add a second backend instance and configure basic load balancing with upstream.<\/li>\n\n\n\n<li>Set up logging (access_log \/ error_log) to monitor proxy traffic.<\/li>\n\n\n\n<li>Harden your config with rate limiting (limit_req) to prevent abuse.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Key Takeaways<\/strong><\/h2>\n\n\n\n<ul>\n<li>An NGINX reverse proxy forwards client requests to backend servers and returns the response.<\/li>\n\n\n\n<li>Use proxy_pass plus the standard proxy_set_header lines to avoid losing client info.<\/li>\n\n\n\n<li>Always run nginx -t before reloading configs in production.<\/li>\n\n\n\n<li>SSL termination at the proxy layer (via Certbot) is now standard practice, not optional.<\/li>\n\n\n\n<li>Common errors like 502s almost always trace back to the backend app, not NGINX itself.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>FAQs<\/strong><\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1782568416572\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>What does a reverse proxy do in NGINX?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>It receives incoming client requests, forwards them to one or more backend servers, and returns the backend&#8217;s response to the client.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1782568428331\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Is NGINX reverse proxy free to use?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes. NGINX (the open-source version) is free under a BSD-style license; only NGINX Plus, the commercial edition, requires a paid license.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1782568429192\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Why am I getting a 502 Bad Gateway error?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>This almost always means your backend application isn&#8217;t running, is on the wrong port, or NGINX can&#8217;t reach it \u2014 check with a direct curl to the backend port.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1782568431985\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Can NGINX reverse proxy multiple domains on one server?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes. You can create separate server blocks for each domain, each with its own proxy_pass target, all on the same NGINX instance.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1782568497760\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Do I need SSL for a reverse proxy?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>It&#8217;s strongly recommended. Terminating SSL at the proxy layer with a tool like Certbot secures traffic without requiring changes to your backend app.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1782568498480\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>What&#8217;s the difference between proxy_pass and upstream in NGINX?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>proxy_pass points to a single backend address, while upstream defines a named pool of backend servers that NGINX can load-balance across.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Most modern websites don&#8217;t expose their backend servers directly to the internet. Instead, they use a reverse proxy to handle incoming traffic, improve security, and manage requests efficiently. NGINX is one of the most popular tools for this job, but a small configuration mistake can quickly lead to frustrating errors like 502 Bad Gateway.&nbsp; This [&hellip;]<\/p>\n","protected":false},"author":64,"featured_media":119502,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[294],"tags":[],"views":"59","authorinfo":{"name":"Abhishek Pati","url":"https:\/\/www.guvi.in\/blog\/author\/abhishek-pati\/"},"thumbnailURL":"https:\/\/www.guvi.in\/blog\/wp-content\/uploads\/2026\/06\/NGINX-Reverse-Proxy-300x116.webp","_links":{"self":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/119355"}],"collection":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/users\/64"}],"replies":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/comments?post=119355"}],"version-history":[{"count":5,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/119355\/revisions"}],"predecessor-version":[{"id":119506,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/119355\/revisions\/119506"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media\/119502"}],"wp:attachment":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media?parent=119355"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/categories?post=119355"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/tags?post=119355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}