Set Up Nginx on Windows

Configure Nginx on Windows to serve your React IoT dashboard and proxy API calls.

Introduction

30 Second Summary

When you visit a website, something has to actually deliver those files to your browser. In production, that "something" is almost always a web server like Nginx sitting in front of your application.

In this project, you will download and configure Nginx on Windows to serve your React IoT dashboard and proxy API requests to your FastAPI backend, all from a single URL.

What You'll Build

A production-style Nginx configuration that acts as the single entry point for your IoT telemetry dashboard, serving static files and reverse-proxying API calls to your backend.

By the end of this project, you'll have:

  • Nginx running natively on Windows, serving your compiled React app as optimized static files.
  • A reverse proxy configuration that transparently forwards /api/ requests to your FastAPI backend on port 8000.
  • A complete understanding of nginx.conf structure, including server blocks, location blocks, and directives like try_files and proxy_pass.
  • Secret Mission: Add gzip compression and browser caching headers to make your dashboard load faster.

Are there any prerequisites?

This project assumes you already have a React app built with Vite, a FastAPI backend running on port 8000, and Node.js/npm installed on your Windows machine.

You do not need any prior Nginx experience. This project walks you through everything from download to a working configuration.

Download and Install Nginx on Windows

Your IoT dashboard currently runs on a development server, and your FastAPI backend listens on a separate port. In production, a web server sits in front of both, acting as a single entry point for all requests.

Nginx is one of the most popular web servers for exactly this pattern. In this step, you'll download Nginx for Windows and get it running on your machine.

In this step, get ready to:
  • Download and extract Nginx for Windows.
  • Start the Nginx server and verify it is running.
  • Practice the core process management commands.
Download and extract Nginx

Nginx provides an official Windows build as a zip file. You'll download the mainline version, which contains all the latest fixes.

  • Open your browser and go to the Nginx download page:
  • https://nginx.org/en/download.html
  • Under the Mainline version heading, click the link labeled nginx/Windows-1.31.2.
  • Save the zip file when prompted.

Why mainline instead of stable?

The official Nginx documentation recommends the mainline version because it contains all known fixes. The stable branch only receives critical backports.

  • Open File Explorer and navigate to your Downloads folder.
  • Right-click the nginx-1.31.2.zip file and select Extract All.
  • Set the destination to C:\ and click Extract.
  • Once extracted, open the C:\nginx-1.31.2 folder and explore its contents.

You should see four key items inside the folder.

  • conf/ contains configuration files including nginx.conf.
  • logs/ stores access and error logs.
  • html/ holds the default web content.
  • nginx.exe is the server itself.

What is nginx.conf?

The nginx.conf file is where all of Nginx's behavior is defined. Every request that comes into Nginx gets routed based on the rules in this single file.

You'll edit this file in the next steps to serve your React app and proxy API requests.

Start Nginx and verify it is running

Nginx on Windows runs as a standard console application. You'll start it from Command Prompt and verify the processes are running.

  • Press the Windows key to open your search bar.
  • Type Command Prompt and press Enter to open it.
  • Navigate to the Nginx folder by running this command:
cd c:\nginx-1.31.2
  • Start Nginx by running this command:
start nginx

What does start nginx do?

The start command launches Nginx in the background so your Command Prompt stays usable. Nginx creates two processes: a master process that manages configuration, and a worker process that handles actual requests.

  • Verify Nginx is running by running this command:
tasklist /fi "imagename eq nginx.exe"

You should see two nginx.exe entries in the output. One is the master process, and the other is the worker process.

  • Open your browser and navigate to http://localhost.

You should see the default "Welcome to nginx!" page. This confirms Nginx is running and serving content on port 80.

Don't see the welcome page?

Port 80 might already be in use by another program like IIS or Skype. Check what is using port 80 by running:

netstat -ano | findstr :80

If another process is using port 80, close that application and try start nginx again. Also check logs\error.log inside your Nginx folder for specific error messages.

help me troubleshoot why Nginx is not starting on Windows

Learn process management commands

You'll edit Nginx's configuration throughout this project. Each time you do, you need to tell Nginx to reload its config. Let's practice the core management commands now.

  • Stop Nginx immediately by running this command:
nginx -s stop

What does -s mean?

The -s flag sends a signal to the running Nginx processes. The word after -s tells Nginx what to do: stop for fast shutdown, quit for graceful shutdown, and reload to apply config changes without stopping.

  • Confirm it stopped by running the tasklist check again:
tasklist /fi "imagename eq nginx.exe"

You should see a message indicating no matching processes were found. This confirms Nginx has shut down.

  • Start Nginx again by running:
start nginx
  • Now practice reloading the configuration by running:
nginx -s reload

This is the command you'll use most often. It applies any changes to nginx.conf without dropping active connections.

  • Refresh http://localhost in your browser to confirm the welcome page still loads.

Quick reference for Nginx signals

  • nginx -s reload applies config changes without stopping the server.
  • nginx -s stop performs a fast shutdown.
  • nginx -s quit performs a graceful shutdown (finishes active requests first).

Seeing a 'command not found' error?

The nginx commands only work when your Command Prompt is inside the C:\nginx-1.31.2 directory. Make sure you ran cd c:\nginx-1.31.2 first.

help me fix the nginx command not being recognized in my Command Prompt

Your web server is up and running. Next up, you'll point Nginx at your React app's build output and serve your IoT dashboard as optimized static files.

Serve Your React Build as Static Files

In the previous step, you got Nginx running and saw its default welcome page. That proves the server works, but it is still serving a placeholder.

Your React IoT dashboard currently runs through a development server, which is great for coding but not how production apps are delivered. In this step, you will compile your React app into optimized static files and configure Nginx to serve them directly to the browser.

In this step, get ready to:
  • Build your React app with Vite to produce optimized static files.
  • Configure nginx.conf to serve the build output with SPA routing support.
  • Reload Nginx and verify your dashboard loads at http://localhost.
Build your React app and locate the output

Vite's npm run build command compiles your React source code into a dist/ folder containing a single index.html file and hashed JavaScript/CSS bundles. These are the static files Nginx will serve.

  • Navigate to your React project folder and build the app by running these commands:
cd [[PROJECT_PATH="C:\Users\your-username\your-project"]]
npm run build

What does this command do?

The cd command moves your terminal into your React project folder.

The npm run build command triggers Vite's production build. It minifies your JavaScript, hashes filenames for cache-busting, and outputs everything into a dist/ folder. This folder is all Nginx needs to serve your entire frontend.

  • After the build finishes, note the full path to your dist/ folder. You will need it in the next substep.
  • The path will look something like C:\Users\your-username\your-project\dist.

Build failed or command not found?

Make sure you are inside the folder that contains your package.json file. If npm is not recognized, Node.js may not be installed or not on your system PATH.

Help me troubleshoot this

Write your server block in nginx.conf

The nginx.conf file controls everything about how Nginx handles requests. You will replace the default configuration with one that points to your React build output.

  • Open conf/nginx.conf in a text editor. This file is inside your C:\nginx-1.31.2 folder.
  • Select all the existing content and delete it.
  • Replace it with the following configuration:
# Number of worker processes (only one handles requests on Windows)
worker_processes  1;

# Connection handling settings
events {
    worker_connections  1024;
}

http {
    # MIME type mappings so browsers interpret files correctly
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        # Point to your React build output (use forward slashes on Windows)
        root [[DIST_PATH="C:/Users/your-username/your-project/dist"]];
        index index.html;

        # SPA fallback: try the file, then directory, then serve index.html
        location / {
            try_files $uri $uri/ /index.html;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

What does this configuration do?

  • root tells Nginx where your static files live. This must be the full path to your dist/ folder using forward slashes, even on Windows.
  • index index.html means requests to / serve index.html by default.
  • try_files $uri $uri/ /index.html is the key to SPA routing. Nginx first tries to find the exact file requested. If no file or directory matches, it serves index.html so React Router can handle the path on the client side.
  • include mime.types loads file extension mappings so your browser knows a .js file is JavaScript and a .css file is a stylesheet.

Why try_files instead of a simple root?

Without try_files, refreshing on a route like /dashboard would make Nginx look for a literal file called dashboard in your dist/ folder. That file does not exist because React handles routing in JavaScript.

The try_files directive catches these requests and sends back index.html instead. React then reads the URL and renders the correct page.

  • Save conf/nginx.conf.

Seeing a path error in your editor?

Make sure your root path uses forward slashes (/), not backslashes (\). Nginx requires UNIX-style forward slashes in its configuration files, even on Windows.

Help me fix my nginx.conf path

Reload Nginx and verify your dashboard

Nginx reads its configuration file only at startup or when you explicitly tell it to reload. Since you changed nginx.conf, you need to signal Nginx to pick up the new settings.

  • Switch back to your Command Prompt and navigate to the Nginx folder by running:
cd C:\nginx-1.31.2
  • Test your configuration for syntax errors by running:
nginx -t
  • You should see a message confirming the syntax is OK and the test is successful.
  • Reload Nginx to apply your new configuration by running:
nginx -s reload

What does nginx -s reload do?

The -s reload signal tells the Nginx master process to re-read nginx.conf and apply changes without dropping any active connections. This is how you update Nginx in production without downtime.

  • Open your browser and visit http://localhost.
  • Your IoT dashboard should load instead of the default Nginx welcome page.
  • Test SPA routing by typing a route directly into your browser's address bar (for example, http://localhost/dashboard). The page should load without a 404 error.

Still seeing the welcome page or getting a 404?

  • Check that the root path in nginx.conf points to the actual dist/ folder that contains index.html. Open that folder in File Explorer to confirm the file is there.
  • Open logs/error.log inside C:\nginx-1.31.2 to see if Nginx reports any configuration errors.
  • Make sure you ran nginx -s reload from within the C:\nginx-1.31.2 folder. Running it from another directory can fail silently.

Help me debug my Nginx configuration

✔️ Awesome, I've got everything!

Great. Double check you have saved conf/nginx.conf and that your dashboard loads at http://localhost.

ⓧ I'd like to double check the full code

Here is the complete conf/nginx.conf for this step. Make sure yours matches (with your own dist/ path):

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        root [[DIST_PATH="C:/Users/your-username/your-project/dist"]];
        index index.html;

        location / {
            try_files $uri $uri/ /index.html;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

Your dashboard is now served by Nginx as optimized static files. Next up, you will wire your API requests through Nginx so the frontend and backend share a single entry point.

Reverse Proxy API Requests to FastAPI

Your IoT dashboard loads at http://localhost, but it still needs live data from your FastAPI backend.

Right now, the React app would need to call a separate port to reach the API. A reverse proxy solves this by making Nginx the single entry point. Nginx forwards API requests to FastAPI behind the scenes, so the browser only ever talks to one URL.

In this step, get ready to:
  • Add a reverse proxy location block to forward API requests to FastAPI.
  • Set proxy headers to preserve client information through the proxy layer.
  • Reload Nginx and verify that API requests flow through to your backend.
Add a /api/ location block

Nginx uses location blocks to decide how to handle different URL paths. You will add a new block specifically for /api/ requests that tells Nginx to pass them to FastAPI instead of serving a file.

  • In your text editor, open conf/nginx.conf inside the C:\nginx-1.31.2 folder.
  • Find the location / block inside your server block:
        location / {
            try_files $uri $uri/ /index.html;
        }
  • Add the following new location block above the location / block:
        location /api/ {
            proxy_pass http://127.0.0.1:8000;
        }

What does this code do?

The location /api/ block matches any request whose path starts with /api/.

The proxy_pass http://127.0.0.1:8000; directive tells Nginx to forward these requests to FastAPI on port 8000. Because there is no trailing slash or URI after the port, the original request path (like /api/bootstrap) passes through unchanged to FastAPI.

You won't see a change yet. The next part adds headers before you reload.

Set proxy headers

When Nginx proxies a request, FastAPI only sees Nginx's IP address by default. Proxy headers fix this by passing the real client information through to the backend.

  • Inside the location /api/ block you just created, add these three lines below proxy_pass:
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

What do these headers do?

  • Host $host passes the original hostname the browser requested so FastAPI can see the correct domain.
  • X-Real-IP $remote_addr sends the client's actual IP address instead of Nginx's loopback address.
  • X-Forwarded-For $proxy_add_x_forwarded_for appends the client IP to a chain of forwarded addresses, which is the standard way to track the original requester through proxy layers.
  • Save conf/nginx.conf.
  • Your complete location /api/ block should now look like this:
        location /api/ {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

✔️ Awesome, I've got everything!

Great. Double check you have saved the file before reloading.

ⓧ I'd like to double check the full code

Here is the complete conf/nginx.conf at this point:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        root C:/Users/your-username/your-project/dist;
        index index.html;

        location /api/ {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location / {
            try_files $uri $uri/ /index.html;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
Reload and test API passthrough

With the proxy configuration saved, you need to reload Nginx so it picks up the changes. Then you can verify that API requests flow through to FastAPI.

  • Switch back to your Command Prompt (still at C:\nginx-1.31.2).
  • Reload Nginx to apply the new configuration by running:
nginx -s reload
  • Make sure your FastAPI backend is still running on port 8000.
  • Open your browser and visit http://localhost/api/bootstrap to test the proxy directly.

You should see the JSON response from FastAPI displayed in the browser. This confirms Nginx is forwarding the request to your backend.

  • Now open http://localhost in a fresh browser tab.
  • Open your browser's DevTools with F12 and click the Network tab.
  • Refresh the page and watch the requests. API calls to paths like /api/bootstrap and /api/dashboard/... should return status 200, meaning they routed through Nginx to FastAPI successfully.

Your dashboard should display live IoT telemetry data. Everything comes through a single URL on port 80.

Seeing a 502 Bad Gateway?

A 502 means Nginx cannot reach the upstream server. Make sure FastAPI is running on port 8000. You can verify by visiting http://127.0.0.1:8000 directly in your browser.

If you see a syntax error when reloading, run nginx -t to check your config. Look for missing semicolons or mismatched braces. Check logs\error.log for details.

Also confirm your location /api/ block sits above the location / block. If it is below, the catch-all / location will match first and serve index.html instead of proxying.

Help me troubleshoot my Nginx reverse proxy returning a 502 error when trying to reach FastAPI on port 8000.

Your IoT dashboard now runs through a single entry point. Nginx serves the React frontend and proxies API calls to FastAPI. Next up, you will explore Nginx logs to understand exactly how each request flows through the server.

Test the Full Stack and Read Nginx Logs

You have Nginx serving your React build and proxying API calls to FastAPI. The two pieces are working individually, but you have not yet verified them as a unified system.

In this step, you will run a full end-to-end check, then learn to read Nginx's log files. Logs are your primary debugging tool when something goes wrong in production.

In this step, get ready to:
  • Verify the entire stack works through a single URL.
  • Read access and error logs to understand request flow.
  • Learn troubleshooting commands for common Nginx issues.
Run an end-to-end verification

With both Nginx and FastAPI running, your entire application should be accessible through a single URL. This is the production pattern: one entry point, multiple services behind it.

  • Open a fresh browser tab and navigate to http://localhost.
  • Confirm your React IoT dashboard loads. This proves Nginx is serving your static files from the dist/ folder.
  • Wait for the dashboard data to populate. This proves API requests are being proxied through Nginx to FastAPI.
  • Type a direct route URL into your browser's address bar (for example, http://localhost/dashboard) and press Enter.
  • Confirm the page loads without a 404 error. This proves try_files is correctly falling back to index.html for client-side routing.

What is happening behind the scenes?

Every request hits Nginx on port 80. Nginx checks the path against your location blocks in order. Paths starting with /api/ get forwarded to FastAPI on port 8000. All other paths are matched by location /, which tries to serve a file from your dist/ folder.

If no matching file exists (like when you type /dashboard directly), try_files serves index.html instead, letting React Router handle the path on the client side.

Dashboard not loading or showing errors?

If you see the Nginx welcome page instead of your dashboard, your root path in nginx.conf does not point to your dist/ folder. Double check the path uses forward slashes.

If data does not populate, confirm FastAPI is still running on port 8000. Open a new Command Prompt and check with tasklist.

help me troubleshoot why my Nginx dashboard is not loading correctly

Read and understand Nginx logs

Nginx records every request it processes. These logs are your window into what the server is actually doing with incoming traffic.

  • Open logs/access.log in a text editor. This file is inside your C:\nginx-1.31.2 folder.

Each line in the access log represents one HTTP request that Nginx processed. The default format includes these fields in order:

  • Client IP address (127.0.0.1 for local requests).
  • Timestamp in square brackets.
  • HTTP method, path, and protocol (for example, GET /index.html HTTP/1.1).
  • Response status code (200 means success).
  • Response size in bytes.
  • Look for requests to .js and .css files. These are your static React assets served directly by Nginx. They should have a 200 status code.
  • Look for requests to /api/ paths. These are the requests Nginx proxied to FastAPI.
  • Open logs/error.log in your text editor. Check for any warnings or error messages.

What should I see in the logs?

A healthy log shows 200 status codes for static assets (.js, .css, index.html) and 200 codes for proxied API calls. If you see 404 errors, it means Nginx could not find the requested file. A 502 error means Nginx could not reach FastAPI.

The error log should be empty or contain only startup messages. Any lines mentioning syntax errors or connection failures point to a misconfiguration.

Access log is empty?

If access.log is empty, make sure you have loaded your dashboard in the browser at least once since starting Nginx. On Windows, Nginx may buffer log writes briefly. Close and reopen the file to see updates.

help me understand why my Nginx access log is empty

Learn key troubleshooting commands

When something breaks, you need a systematic way to diagnose the issue. These three commands cover the most common Nginx problems on Windows.

  • Test your configuration syntax without reloading by running this command:
nginx -t

What does this command do?

The nginx -t command parses your nginx.conf and checks for syntax errors without actually reloading the server. Run this every time you edit the config file. If there is an error, it tells you the exact line number.

  • Check if another process is using port 80 by running this command:
netstat -ano | findstr :80

What does this command do?

This command lists all processes listening on port 80. If Nginx fails to start, another program (like IIS or Skype) might already occupy that port. The last column shows the process ID (PID) so you can identify and stop the conflicting process.

  • Always check logs/error.log after a failed nginx -s reload. The error log tells you exactly what went wrong and on which line.
  • Verify FastAPI is running before testing API routes. If /api/ paths return 502 errors, it means the backend is down.
  • Confirm all paths in nginx.conf use forward slashes, even on Windows. Backslashes cause parsing failures.

Build a debugging habit

The workflow for any Nginx change is: edit nginx.conf, run nginx -t to check syntax, then nginx -s reload to apply. If the reload fails, check logs/error.log for the exact error. Never skip the nginx -t step.

Secret mission

Add Gzip Compression and Cache Headers

Your dashboard works, but every byte counts on a real network. Can you configure Nginx to compress responses and tell browsers to cache static assets, cutting load times for your IoT dashboard?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

This project runs entirely locally, so there are no ongoing cloud costs. Nginx uses minimal system resources while running, but you may want to stop it to free up port 80.

Resources you used:

  • Nginx installation folder (C:\nginx-1.31.2).
  • Nginx running processes (master and worker).
  • React build output (dist/ folder in your React project).

Keep everything running

No action needed. Choose this if you are still actively building your IoT dashboard or want Nginx available as your local web server.

  • Nginx continues running in the background, serving your dashboard at http://localhost.
  • Your conf/nginx.conf stays configured with gzip compression, cache headers, and reverse proxying.
  • If your machine restarts, Nginx will not auto-start. You will need to navigate to C:\nginx-1.31.2 and run start nginx again.

Pause - I'll come back to this later

Shut down Nginx to free up port 80, but keep all your files and configuration intact so you can restart instantly.

  • Open a Command Prompt and navigate to your Nginx folder by running:
cd C:\nginx-1.31.2
  • Stop Nginx by running:
nginx -s stop
  • When you are ready to resume, navigate back to the same folder and run start nginx to start the server again.

Delete - I don't want to use this again

Remove all project resources and start fresh. This stops Nginx and deletes the entire installation folder.

  • Open a Command Prompt and navigate to your Nginx folder:
cd C:\nginx-1.31.2
  • Stop Nginx:
nginx -s stop
  • Move up one directory and delete the entire Nginx folder:
cd C:\
rmdir /s /q nginx-1.31.2
  • Delete the dist/ build output folder inside your React project:
rmdir /s /q C:\Users\your-username\your-project\dist
  • You can regenerate the dist/ folder at any time by running npm run build in your React project.

Nice Work!

Nice Work!

You just configured Nginx as a production-style web server on Windows, serving your React IoT dashboard and proxying API requests through a single entry point.

You've learned how to:

  • Configured Nginx to serve your Vite-built React app as static files with SPA routing support using the try_files directive.
  • Set up a reverse proxy to forward /api/ requests to your FastAPI backend with proper proxy headers preserving client information.
  • Practiced the full Nginx workflow: editing nginx.conf, testing syntax with nginx -t, and applying changes with nginx -s reload.
  • Secret Mission: Added gzip compression and browser cache headers for production-level performance on your IoT dashboard.

Ready to quiz yourself?