Skip to content Skip to footer

Nginx: Building a Scalable Multi-Service Architecture with Subdomain Routing

Nginx Reverse Proxy

Modern web applications rarely exist in isolation. Today’s digital infrastructure often consists of multiple interconnected services—APIs, databases, admin panels, monitoring tools, and more. Managing these services efficiently while maintaining security and performance can be challenging. This is where Nginx’s powerful reverse proxy capabilities shine, particularly when combined with subdomain-based routing.

The Challenge: Managing Multiple Services

Imagine you’re running a web application with several backend services:

  • A main web application
  • A RESTful API
  • An analytics dashboard
  • A file management system
  • Various microservices that support your platform

Traditionally, you might expose these on different ports (:8080, :3000, :9000, etc.), but this approach has significant drawbacks:

  • Users must remember port numbers
  • Firewalls often block non-standard ports
  • SSL certificate management becomes complicated
  • The architecture looks unprofessional and is harder to maintain

The Solution: Subdomain-Based Routing with Nginx

A more elegant approach uses subdomain routing, where each service gets its own subdomain under your main domain. Instead of example.com:8080, you access api.example.com. This creates a clean, professional structure that’s easier to manage and more secure.

The Architecture Overview

The setup relies on three key components working together:

1. Wildcard DNS Configuration

First, configure a wildcard DNS record that points all subdomains to your server’s IP address:

*.example.com  →  203.0.113.45
example.com    →  203.0.113.45

This single DNS record means any subdomain—whether it’s api.example.com, admin.example.com, or newservice.example.com—automatically resolves to your server without additional DNS configuration.

2. Wildcard SSL Certificate

Security is non-negotiable in modern web infrastructure. A wildcard SSL certificate covers your root domain and all subdomains with a single certificate:

Certificate covers:
  - example.com
  - *.example.com

This means HTTPS works automatically for any subdomain you configure, eliminating the need to obtain and manage separate certificates for each service.

3. Nginx Reverse Proxy Configuration

Nginx sits at the front of your infrastructure, receiving all incoming requests and intelligently routing them to the appropriate backend service based on the subdomain.

Implementation: A Complete Nginx Configuration

Let’s break down a production-ready Nginx configuration that demonstrates these concepts:

Global Performance Optimization

# Enable compression for faster page loads
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types text/plain text/css text/javascript application/json;

Enabling gzip compression reduces bandwidth usage and improves load times, especially for text-based content like HTML, CSS, and JSON.

Defining Backend Services

# Define upstream servers
upstream api_backend {
    server api_service:8080;
}

upstream analytics_backend {
    server analytics_service:3000;
}

upstream admin_backend {
    server admin_service:5000;
}

Upstream blocks define your backend services. These can be Docker containers, separate servers, or services running on different ports on the same machine. Nginx abstracts away these implementation details from your users.

Centralized SSL Configuration

# Common SSL configuration
ssl_certificate /etc/ssl/certs/wildcard.crt;
ssl_certificate_key /etc/ssl/private/wildcard.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

This configuration applies to all server blocks, ensuring consistent security across your entire infrastructure. Modern TLS protocols and cipher suites protect your data in transit.

Automatic HTTP to HTTPS Redirect

# HTTP to HTTPS redirect
server {
    listen 80;
    server_name _;
    server_tokens off;
    return 301 https://$host$request_uri;
}

This catch-all block ensures that any HTTP request is automatically redirected to HTTPS, enforcing encryption for all traffic.

Main Application Server

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;
    server_tokens off;

    # Performance tuning
    client_max_body_size 100M;
    client_header_buffer_size 64k;
    large_client_header_buffers 4 64k;

    # Serve static content
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
        
        # Cache static assets aggressively
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
            access_log off;
        }
    }

    # Proxy API requests to backend
    location /api/ {
        proxy_pass http://api_backend/;
        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_redirect off;
    }
}

This main server block handles your primary domain, serving static files directly while proxying API requests to backend services.

Subdomain Routing

Here’s where the real power comes in—dedicated server blocks for each subdomain:

# API subdomain
server {
    listen 443 ssl;
    http2 on;
    server_name api.example.com;
    server_tokens off;

    location / {
        proxy_pass http://api_backend;
        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;
        
        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        # Performance tuning
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        proxy_buffering off;
    }
}

# Analytics subdomain
server {
    listen 443 ssl;
    http2 on;
    server_name analytics.example.com;
    server_tokens off;

    location / {
        proxy_pass http://analytics_backend;
        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;
    }
}

# Admin panel subdomain
server {
    listen 443 ssl;
    http2 on;
    server_name admin.example.com;
    server_tokens off;

    location / {
        proxy_pass http://admin_backend;
        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;
    }
}

Each subdomain gets its own server block, allowing you to customize proxy settings, timeouts, and headers specifically for that service’s needs.

Catch-All Protection

# Catch-all for unknown subdomains
server {
    listen 443 ssl;
    http2 on;
    server_name *.example.com;
    server_tokens off;
    return 302 https://example.com;
}

This safety net redirects any undefined subdomain back to your main site, preventing errors and potential security issues from unmanaged subdomains.

Key Benefits of This Architecture

1. Simplified SSL Management

One wildcard certificate secures your entire infrastructure. No more juggling multiple certificates with different expiration dates.

2. Clean, Professional URLs

api.example.com is far more professional than example.com:8080 and easier for users to remember and share.

3. Easy Service Addition

Adding a new service requires only:

  • Defining a new upstream block
  • Creating a new server block
  • Reloading Nginx configuration

No DNS changes needed—the wildcard DNS record already covers it.

4. Security Isolation

Each service can have different security policies, rate limiting, and access controls. Compromising one service doesn’t automatically expose others.

5. Flexible Backend Architecture

Backend services can be:

  • Docker containers
  • Separate physical servers
  • Cloud instances
  • Different ports on the same machine

Nginx abstracts this complexity from your users.

6. Performance Optimization

Nginx handles:

  • SSL termination (freeing backend services from encryption overhead)
  • HTTP/2 multiplexing
  • Static file caching
  • Response compression
  • Connection pooling

7. Zero Downtime Deployments

You can update backend services independently without affecting the Nginx frontend. Users experience no interruption.

8. Advanced Features Ready

This foundation makes it easy to add:

  • Load balancing across multiple backend instances
  • Rate limiting per subdomain
  • Geographic routing
  • A/B testing
  • WebSocket support
  • Custom error pages

Real-World Example: Adding a New Service

Let’s say you want to add a monitoring dashboard. Here’s all you need to do:

  1. Deploy your monitoring service (e.g., on port 9090)
  2. Add to Nginx configuration:
upstream monitoring_backend {
    server monitoring_service:9090;
}

server {
    listen 443 ssl;
    http2 on;
    server_name monitoring.example.com;
    server_tokens off;

    location / {
        proxy_pass http://monitoring_backend;
        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;
    }
}
  1. Reload Nginx:
nginx -t && nginx -s reload

That’s it! https://monitoring.example.com is now live, secured with SSL, and accessible to users.

Best Practices and Considerations

Security Headers

Always include security headers in your configuration:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

Logging

Maintain separate access and error logs for each subdomain to simplify troubleshooting:

access_log /var/log/nginx/api.access.log;
error_log /var/log/nginx/api.error.log;

Rate Limiting

Protect your services from abuse with rate limiting:

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

location / {
    limit_req zone=api_limit burst=20;
    proxy_pass http://api_backend;
}

Health Checks

Configure proper upstream health checks to ensure traffic only goes to healthy backends:

upstream api_backend {
    server api_service:8080 max_fails=3 fail_timeout=30s;
}

Conclusion

Subdomain-based routing with Nginx provides a robust, scalable foundation for modern multi-service architectures. By combining wildcard DNS, wildcard SSL certificates, and Nginx’s powerful reverse proxy capabilities, you create an infrastructure that’s:

  • Professional: Clean URLs that inspire confidence
  • Secure: Centralized SSL management and isolated services
  • Scalable: Easy to add new services without infrastructure changes
  • Performant: Nginx handles optimization at the edge
  • Maintainable: Clear separation of concerns and configuration

Whether you’re running a small web application with a few microservices or a complex platform with dozens of interconnected services, this architecture pattern provides the flexibility and robustness you need to grow confidently.

The beauty of this approach is that it starts simple but scales infinitely. Begin with a few subdomains and expand as your needs grow—all without fundamental changes to your architecture.

Leave a Comment