← Back to Blog List

Building a Virtual Host and Reverse Proxy Simulator with Docker

May 12, 2026

3 min read

This project demonstrates how to manage multiple domains on a single server using Nginx as a Reverse Proxy and Docker for infrastructure isolation. The goal is to route traffic for domain-a.com (static content) and domain-b.com (dynamic containerized service) through a single entry point.


Core Concepts

Virtual Hosting & HTTP Host Headers

Web servers use the HTTP Host Header to distinguish between multiple domains sharing the same IP address. When a browser sends a request, it includes a header like Host: domain-a.com. Nginx inspects this header and matches it against the server_name directive in its configuration to decide which content to serve.

Reverse Proxy

Instead of serving files directly, a Reverse Proxy intercepts requests and forwards them to another backend server or container. This is essential for load balancing, security, and managing microservices.

blog-image

Project Architecture

  • Entry Point: Port 80 on 127.0.0.1 (Localhost).
  • Web Server: Nginx (Containerized).
  • Backend Service: A lightweight HTTP Echo container.
  • Network: A custom Docker bridge network (proxy-net) for secure internal communication.

Project Structure

simulator-proxy/
├── docker-compose.yml     # Infrastructure Orchestration
├── default.conf           # Nginx Routing Logic
└── html/                  # Static Content Folders
    ├── domain-a/index.html
    └── domain-b/index.html

Implementation Details

Docker Infrastructure (docker-compose.yml)

We define two services. Notice the networks block, which enables Service Discovery, allowing Nginx to find the app container by its name.

services:
  web-server:
    image: nginx:alpine
    container_name: nginx-simulator
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
      - ./default.conf:/etc/nginx/conf.d/default.conf
    networks:
      - proxy-net

  app-service:
    image: hashicorp/http-echo
    container_name: internal-app
    command: ["-text=Hello! I am the internal application."]
    networks:
      - proxy-net

networks:
  proxy-net:
    driver: bridge

Nginx Configuration (default.conf)

The configuration handles two different routing strategies:

  1. Direct Root: Serving static files for Domain A.
  2. Proxy Pass: Forwarding traffic for Domain B to the internal-app container.
server {
    listen 80;
    server_name domain-a.com;

    location / {
        root /usr/share/nginx/html/domain-a;
        index index.html;
    }
}

server {
    listen 80;
    server_name domain-b.com;

    location / {
        proxy_pass http://internal-app:5678;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Deployment Steps

blog-image
  1. Simulate DNS: Edit your local /etc/hosts (Linux/Mac) or C:\Windows\System32\drivers\etc\hosts (Windows) to map the domains to your local machine:Plaintext127.0.0.1 domain-a.com 127.0.0.1 domain-b.com
  2. Launch Containers: docker compose up -d
  3. Verify Connectivity:
    • Visit http://domain-a.com to see the static HTML.
    • Visit http://domain-b.com to see the response proxied from the internal app.
blog-image

Conclusion

By using Docker and Nginx, we successfully simulated a production-grade environment. We learned that Port 80 acts as the gateway, the Host Header acts as the sorter, and Docker Networks act as the internal communication lines that keep the services connected yet isolated.

← Back to Blog List