DEV Community

Roshan singh
Roshan singh

Posted on

# The Journey of a Request: How NGINX Routes Traffic Between Docker Containers in a Modern Web Application

The Journey of a Request: From Browser to Database and Back

Have you ever wondered what actually happens after you type a website URL into your browser?

Let's follow the complete journey of a request in a typical production web application.


Our Production Architecture

Imagine we have four Docker containers running on the same server.

                           Docker Network
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│                    ┌──────────────────┐                             │
│                    │      NGINX       │                             │
│                    │   (Port 80/443)  │                             │
│                    └────────┬─────────┘                             │
│                             │                                       │
│              ┌──────────────┴──────────────┐                        │
│              │                             │                        │
│              ▼                             ▼                        │
│      ┌───────────────┐             ┌───────────────┐                │
│      │   Frontend    │             │    Backend    │                │
│      │    :3000      │             │     :5000     │                │
│      └───────────────┘             └───────┬───────┘                │
│                                            │                        │
│                                            ▼                        │
│                                   ┌────────────────┐                │
│                                   │   PostgreSQL   │                │
│                                   │     :5432      │                │
│                                   └────────────────┘                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

                    ▲
                    │
            Internet (Browser)
Enter fullscreen mode Exit fullscreen mode

Only NGINX is exposed to the internet.

The Frontend, Backend, and PostgreSQL containers communicate over Docker's internal network. Users can never access these containers directly.

A minimal NGINX reverse proxy configuration looks like this:

server {
    listen 80;
    server_name myapp.com;

    # Website requests
    location / {
        proxy_pass http://frontend:3000;
    }

    # API requests
    location /api/ {
        proxy_pass http://backend:5000;
    }
}
Enter fullscreen mode Exit fullscreen mode

This configuration tells NGINX:

  • Requests to / should go to the Frontend container.
  • Requests beginning with /api should go to the Backend container.

This makes NGINX the single entry point for every incoming request.

Now let's follow an actual request from start to finish.


Step 1: The User Opens the Website

The user enters:

https://myapp.com
Enter fullscreen mode Exit fullscreen mode

The browser doesn't immediately know where this website is hosted.

It first asks the DNS (Domain Name System):

"What's the IP address for myapp.com?"

DNS responds with the server's public IP address.

User
   │
   ▼
Browser
   │
DNS Lookup
   │
Returns Server IP
Enter fullscreen mode Exit fullscreen mode

Now the browser knows exactly where to send the request.


Step 2: The Browser Sends the First Request

The browser sends its first HTTP request:

GET /
Enter fullscreen mode Exit fullscreen mode

This request reaches the server.

Since only NGINX is exposed to the internet, it receives every incoming request first.

Browser
    │
GET /
    │
NGINX
Enter fullscreen mode Exit fullscreen mode

Think of NGINX as the receptionist of a company.

Every visitor first speaks to the receptionist before being directed to the correct department.


Step 3: NGINX Routes the Request to the Frontend

NGINX examines the request.

Since the request is for /, it forwards it to the frontend application (for example, React, Angular, Vue, or Next.js).

Browser
     │
NGINX
     │
Frontend Application
Enter fullscreen mode Exit fullscreen mode

The frontend returns:

  • HTML
  • CSS
  • JavaScript
  • Images
  • Fonts

NGINX sends these files back to the browser.


Step 4: The Browser Builds the User Interface

The browser downloads all the assets.

It parses the HTML, loads the CSS, executes the JavaScript, and finally renders the application on the screen.

At this point, the user can interact with the application.


Step 5: The User Clicks a Button

Suppose the user clicks Login.

Or opens the Users page.

Or clicks View Profile.

The frontend now needs data from the backend.

Instead of contacting the backend directly, it makes an API request like:

POST /api/login
Enter fullscreen mode Exit fullscreen mode

or

GET /api/users
Enter fullscreen mode Exit fullscreen mode

Notice something important.

The browser sends the request to the same domain:

https://myapp.com/api/users
Enter fullscreen mode Exit fullscreen mode

It does not know anything about internal services such as:

backend:5000
Enter fullscreen mode Exit fullscreen mode

Those names exist only inside Docker's private network.


Step 6: NGINX Routes the API Request

The API request once again reaches NGINX.

Browser
     │
GET /api/users
     │
NGINX
Enter fullscreen mode Exit fullscreen mode

NGINX recognizes that the request begins with /api.

Instead of sending it to the frontend, it forwards it internally to the backend application (for example, Express, NestJS, Spring Boot, Django, ASP.NET Core).

Browser
      │
NGINX
      │
Backend Application
Enter fullscreen mode Exit fullscreen mode

The browser never communicates directly with the backend.


Step 7: The Backend Processes the Request

Inside the backend, the request flows through different layers.

Request
   │
Controller / Route Handler
   │
Service Layer
   │
Repository / ORM
Enter fullscreen mode Exit fullscreen mode

Each layer has its own responsibility.

  • Controller / Route Handler receives the HTTP request.
  • Service Layer contains the business logic.
  • Repository / ORM communicates with the database.

This separation of concerns makes applications easier to maintain, test, and scale.


Step 8: The ORM Talks to the Database

Instead of writing raw SQL everywhere, most modern applications use an ORM (such as Prisma, Hibernate, Sequelize, SQLAlchemy, or Entity Framework).

The ORM converts application code into SQL queries.

Application Code
       │
       ▼
ORM
       │
       ▼
SQL Query
       │
       ▼
Database
Enter fullscreen mode Exit fullscreen mode

The database executes the SQL query and returns the requested data.


Step 9: The Response Begins Its Journey Back

The database sends the result back to the ORM.

The ORM returns it to the Service.

The Service returns it to the Controller.

The Controller creates the HTTP response.

Database
      │
ORM
      │
Service
      │
Controller
Enter fullscreen mode Exit fullscreen mode

The backend has now finished processing the request.


Step 10: NGINX Sends the Response Back

The backend sends its response back to NGINX.

NGINX forwards it to the browser.

Backend
     │
NGINX
     │
Browser
Enter fullscreen mode Exit fullscreen mode

The browser receives the JSON response.

The frontend updates the UI automatically, and the user sees the latest data.


Complete Journey

User
 │
 ▼
Browser
 │
 ▼
DNS Lookup
 │
 ▼
Server IP Address
 │
 ▼
NGINX (Reverse Proxy)
 │
 ├────────────────────────► Frontend Application
 │                               │
 │◄────── HTML / CSS / JS ───────┘
 │
 ▼
Browser Renders UI
 │
 ▼
User Clicks Button
 │
 ▼
GET /api/... or POST /api/...
 │
 ▼
NGINX
 │
 ▼
Backend Application
 │
 ▼
Controller / Route Handler
 │
 ▼
Service Layer
 │
 ▼
Repository / ORM
 │
 ▼
Database
 │
 ▲
ORM
 │
 ▲
Service
 │
 ▲
Controller
 │
 ▲
NGINX
 │
 ▲
Browser
 │
 ▲
Frontend Updates UI
Enter fullscreen mode Exit fullscreen mode

Final Takeaways

  • DNS only tells the browser where the server is located.
  • NGINX is the single public entry point for every request.
  • NGINX decides whether a request should go to the Frontend or the Backend.
  • The browser never communicates directly with backend services or the database.
  • Backend services communicate with the database over Docker's private network.
  • The response follows the exact same path back to the browser.

Once you understand this request journey, modern production architectures become much easier to reason about. Whether you're using React, Angular, Vue, Express, Spring Boot, Django, or any other framework, the overall request lifecycle remains fundamentally the same.

Top comments (0)