A proxy and a load balancer may occupy almost the same place in a network diagram, which is why the two terms are often mixed up. Each can receive a connection before it reaches an application and pass that traffic onward, but the reason for placing each component there is different.
Think of a proxy as a controlled gateway between a requester and a destination. Depending on the configuration, that gateway can conceal internal addresses, handle TLS, cache content, inspect traffic, or choose an application by host and path. A load balancer is focused on another concern: choosing one member of a backend pool for each connection or request so capacity can be shared and a failed node can be bypassed.
These roles are not mutually exclusive. Products such as Nginx, HAProxy, and Traefik can act as the public reverse-proxy layer while also selecting among several instances of the same service.
The sections below build the picture from the network edge inward: forward and reverse proxies, Layer 4 and Layer 7 balancing, common scheduling methods, health checks, Nginx and HAProxy examples, and practical layouts for a VPS, API, website, or system that needs to scale.
Build a Scalable Entry Layer with Falconcloud
A cloud setup is useful when the entry layer may later grow from one proxy into several application nodes and dedicated balancing components.
In Falconcloud, you can provision separate VPS instances for the gateway and application tiers instead of forcing every role onto one machine. Choose resources for Nginx, HAProxy, Traefik, APIs, or supporting services independently, while retaining control over the operating system and installed software. New VMs can be introduced later as capacity or topology requirements change.
A project might begin with Nginx and the app on one virtual machine. Later, the public entry point can be moved onto its own VPS while two or more application servers are added behind it.
Launch a VPS in Falconcloud and use it as the foundation for a site, API, proxy, or other server-side workload.
Proxy Server Explained: What It Does in a Network
A proxy is a network intermediary: the client communicates with the proxy, and the proxy establishes or forwards communication toward the actual destination.
Without an intermediary, the path is simply:
Putting a proxy in front introduces another hop:
The intermediary accepts the request first. It may inspect or rewrite parts of it, choose a destination, relay it, and return the resulting response through the same controlled path.
The exact feature set depends on the product and placement, but a proxy may be used to:
- keep client-side or internal backend addresses out of direct view;
- enforce access rules at one gateway;
- inspect and reject unwanted requests;
- serve cacheable responses without contacting the application every time;
- handle HTTPS and certificate processing at the edge;
- send traffic to different applications according to routing rules;
- normalize, append, or replace HTTP headers;
- record requests before they reach internal services;
- keep application ports reachable only from trusted network segments.
The direction of the relationship matters. In practice, two patterns are discussed most often: a forward proxy representing clients and a reverse proxy representing servers.
Forward Proxy: An Intermediary Chosen for the Client
A forward proxy sits on the requester side of the connection. External destinations see the proxy making the outbound connection instead of seeing the client connect to them directly.
A simplified path is:
From the destination's perspective, the immediate peer is the proxy. The user's device remains behind that intermediary.
Typical reasons for deploying one include:
- sending employee web access through a centrally managed egress point;
- applying policy to outbound connections;
- blocking or allowing destinations centrally;
- reusing cached responses for repeatedly requested resources;
- making selected applications leave the network through a predictable public IP;
- reducing direct outbound exposure from devices inside the private network.
This pattern is mainly about client egress, not ordinary web hosting. For a public website or API, the more relevant pattern is usually a reverse proxy placed in front of the service.
Reverse Proxy: One Public Front Door for Backend Services
A reverse proxy belongs to the service side of the architecture and becomes the address clients reach first.
One public endpoint can therefore represent several internal destinations:
Clients only need the external hostname. The reverse proxy maps that request to the correct internal target, so private service addresses can remain an implementation detail.
A simple routing plan might be:
portal.edge-lab.example/api/ → API
ops.edge-lab.example → admin panel
The same idea works whether several applications share one VPS or each backend lives on a different machine.
Use a Falconcloud VPS as the Reverse-Proxy Entry Point
When the proxy should be isolated from the application tier, it can run on its own cloud VM with Nginx, HAProxy, Traefik, or another gateway product.
A FalconcloudVPS can be sized specifically for an edge role and used as the public endpoint for a website, API, or group of private services. Because you administer the server, you can define routing, certificate handling, firewall policy, caching, and proxy behavior around the needs of the application.
The design can stay small at first or become a separate gateway tier later: the same basic proxy concept still applies when requests are eventually spread across several application hosts.
Provision a cloud server in Falconcloud and build the reverse-proxy layer around the routing and security needs of your project.
Load Balancer Explained: Why Backend Pools Need One
A load balancer places several equivalent backends behind one logical service and decides which backend should handle each new request or connection.
With only one application node, the request path may end at that machine:
That server then carries the entire workload and also becomes the only application target. Saturation or failure directly affects users.
Adding a balancing tier changes the path to:
Because the service is represented by a pool, one unhealthy member can be taken out of rotation while other members continue accepting traffic.
In practical deployments, the balancing layer is responsible for several jobs:
- share traffic among several service instances;
- stop selecting nodes that are known to be unhealthy;
- make additional application instances useful as soon as they join the pool;
- avoid concentrating all new work on one backend;
- present the pool to clients as one service endpoint.
Reverse Proxy vs. Load Balancer: Compare Their Responsibilities
Their positions can look identical on a diagram, so the clearest distinction is functional: a reverse proxy is an intermediary and router, while a load balancer is a selector among multiple service instances.
| Criterion | Reverse Proxy | Load Balancer |
|---|---|---|
| Core responsibility | Gateway functions and request routing | Choosing among members of a backend pool |
| Backend requirement | Still useful with one backend | Requires multiple targets to distribute work |
| TLS termination | Yes | Available in many implementations |
| Caching | Often part of the role | Usually secondary to balancing |
| Health checks | Product- and configuration-dependent | Central to reliable pool operation |
| Scale-out support | Can participate in it | A principal reason to deploy it |
Software does not have to fit into only one column. A single Nginx process can provide reverse-proxy features and balancing logic at the same time.
A simple routing plan might be:
Nginx
Frontend
API
Storage
Here, Nginx is acting as a reverse proxy because it is routing to different services rather than distributing traffic across replicas of one service.
Now suppose that the API itself has three equivalent replicas:
Nginx
For the API route, the same Nginx instance is now doing load balancing as well as reverse proxying.
Does a Reverse Proxy Make Sense with Only One Backend?
Absolutely.
It is actually one of the simplest and most common VPS layouts.
A single host might run several processes on private local ports:
Node.js :3200
Grafana :3201
API :8100
Those application ports do not all need public firewall rules.
Instead, Nginx can own the public HTTP/HTTPS sockets and route by hostname:
api.edge-lab.example → localhost:8100
metrics.edge-lab.example → localhost:3201
There is no distribution algorithm in this layout. Nginx is simply the gateway to one target per route, so it is a reverse proxy without a backend pool.
Can One Product Provide Both Reverse Proxying and Balancing?
For application-layer traffic, very often yes.
Modern gateway products frequently bundle both capabilities.
Common products that can fill this role include:
- Nginx;
- HAProxy;
- Traefik;
- Caddy;
- Envoy.
Such software can terminate the client-side connection, evaluate routing rules, and then either forward to one service or choose one instance from a service pool.
This is why architecture discussions are more useful when framed around required edge functions rather than forcing a strict product label:
Layer 4 vs. Layer 7 Load Balancing: Where the Decision Is Made
A major design choice is how much of the connection the balancer understands before it chooses a backend.
For web infrastructure, that comparison is usually expressed as Layer 4 versus Layer 7.
Layer 4 Balancing: Route by Transport Information
At Layer 4, backend selection is based on transport-level information such as TCP or UDP endpoints rather than application semantics.
The device can forward the connection without parsing an HTTP path, cookie, or method.
A minimal L4 path can be shown as:
Because it does not depend on HTTP semantics, the same concept applies to many non-HTTP services.
What can be balanced depends on the implementation, but L4 products can work with a range of TCP and, where supported, UDP workloads.
Why choose this approach:
- backend selection does not require parsing the application request;
- the design is not tied exclusively to HTTP;
- it fits services where host/path-aware routing would add no value.
The trade-off is reduced application awareness: the balancer has fewer signals available for routing decisions.
For example, it cannot natively express HTTP routing logic such as:
/images → another cluster
unless another layer parses the HTTP request and makes that distinction.
Layer 7 Balancing: Route with Application Context
Layer 7 balancing understands the application protocol well enough to make content-aware decisions.
With HTTP, routing rules may inspect values such as:
- the requested hostname;
- the path or URL;
- method;
- selected headers;
- cookie values;
- other fields exposed by the HTTP request.
For instance, an HTTP-aware rule could be:
portal.edge-lab.example/static/* → Static Pool
ops.edge-lab.example/* → Admin Pool
That extra context makes L7 a natural fit for web applications, API endpoints, and microservice gateways where different requests may need different pools.
Choosing Between L4 and L7 for Your Traffic
For ordinary websites and REST-style APIs, Layer 7 is usually the most useful starting point because the edge can understand HTTP.
Layer 4 is worth considering when:
- the service is TCP/UDP rather than an HTTP application;
- routing does not depend on hostnames, paths, cookies, or methods;
- encryption must remain end-to-end until the selected backend;
- the application protocol is not something the proxy should parse.
Layer 7 gives you more control when the edge must provide:
- host- or path-based routing;
- TLS termination;
- redirect handling;
- rules based on headers or cookies;
- caching;
- request-aware access logging;
- separate pools for different areas of the same public service.
How a Load Balancer Chooses the Next Backend
Once the pool contains more than one healthy target, the balancer needs a scheduling rule for deciding where new work goes.
Round Robin
This is the easiest scheduling method to visualize.
The balancer cycles through the pool in order:
Request 2 → Server B
Request 3 → Server C
Request 4 → Server A
Round Robin is a reasonable default when the nodes are comparable in capacity and individual requests tend to consume similar resources.
Weighted Round Robin
When the machines are not equally powerful, the scheduler can be given relative weights instead of treating every node identically.
A weighted pool might be defined as:
Server B weight=1
The larger weight causes Server A to be selected more frequently over time.
That is useful when the pool mixes instance sizes or generations with different CPU and memory capacity.
Least Connections
Least Connections looks at current concurrency and favors the backend that is handling the smallest number of active connections.
It can outperform simple rotation when some requests finish quickly while others keep connections busy for much longer.
Suppose the active-connection counts currently look like this:
Server B → 43 connections
New request → Server B
IP Hash
IP Hash derives the backend choice from the client's source address.
As long as the relevant inputs and pool remain stable, repeated connections from the same source tend to land on the same node.
That behavior can provide a basic form of affinity, but it is not a perfect session mechanism. Many clients may share one NAT address, while mobile or roaming users can appear from different addresses over time.
Hash-Based Routing with a Custom Key
Some products can hash a value other than the source IP, for example:
- cookie;
- URI;
- an HTTP header;
- a stable client or tenant identifier.
A custom hash is useful when a workload benefits from deterministic placement—for example, keeping the same tenant or resource key on the same backend when possible.
Health Checks: How the Balancer Knows a Backend Is Usable
A pool only improves availability if failed members stop receiving new work. Otherwise, the balancer simply spreads errors along with successful requests.
Health checks provide the signal used to keep unhealthy nodes out of rotation.
A balancer can probe each target on a schedule, for example:
↓
GET /health
↓
Server A → 200 OK
Server B → 200 OK
Server C → timeout
If Server C fails the configured threshold, it can be marked unavailable and excluded until later checks show that it has recovered.
A successful TCP handshake does not necessarily prove that the application can actually serve a real request.
For example, the process may still be listening even though its database, queue, or another mandatory dependency is unavailable.
For HTTP services, a purpose-built readiness endpoint gives a more meaningful signal:
Another common path is:
The endpoint should return success only when the instance is in a state where the balancer may safely send user traffic to it.
Active vs. Passive Backend Failure Detection
There are two broad ways to decide that a target is unhealthy: probe it deliberately or infer failure from production traffic.
Passive Checks: Learn from Real Requests
With passive detection, the proxy watches the outcome of normal connections and requests.
Repeated connection failures, resets, or missing responses can push a backend over the failure threshold and take it out of rotation for a period.
This avoids generating a separate stream of probe requests.
The downside is that real user traffic is what reveals the problem, so some failed requests may occur before the backend is sidelined.
Active Checks: Probe the Backend Before Users Do
With active checking, the balancer generates its own periodic request to a known endpoint:
A failing instance can therefore be removed based on probe results rather than waiting for another production request to discover the failure.
Exact thresholds, probe types, and recovery behavior vary by product, edition, and version, so the available options should be checked for the specific deployment.
Sticky Sessions and Session Affinity Explained
The easiest backend pool to scale is stateless: any healthy instance can handle the next request regardless of which node served the previous one.
Some applications instead keep login or session data only in the RAM of the process that created it.
Consider a session that exists only on one application node:
Session stored in RAM
If a later request is balanced to Server 2, that second instance may have no record of the session and can treat the same user as unauthenticated.
Session affinity works around that design by repeatedly mapping the same client to the same backend whenever possible:
Server 1
Server 2
Affinity can solve the immediate symptom, but it also makes the session dependent on the availability of the selected node.
A more flexible design stores shared state outside the application process:
Redis / Database
With shared state, the balancer is free to send the next request to any healthy application instance.
TLS Termination: Where HTTPS Encryption Ends
HTTPS does not have to be decrypted by every application instance. The reverse proxy or balancing tier can own the client-facing TLS session.
On the public side, the path remains encrypted:
↓
HTTPS
↓
Reverse Proxy
From the proxy toward the application, there are two common designs.
Option one uses plaintext on the trusted internal hop:
↓
HTTP
↓
Application
Option two starts another encrypted connection to the backend:
↓
HTTPS
↓
Application
Internal HTTP can be simpler when both components share one machine or a network segment that is explicitly treated as trusted.
Re-encrypting the upstream connection keeps traffic protected after it leaves the edge, which may be required across multiple hosts, untrusted segments, or stricter compliance environments.
Centralizing TLS at the edge can simplify several operational tasks:
- certificates and renewal logic can be managed at the gateway;
- application instances can remain private;
- multiple hostnames can share one controlled HTTPS entry layer;
- redirect and security-header policy can be applied consistently before traffic reaches the apps.
Preserving Client Information with Proxy Headers
After traffic passes through a reverse proxy, the backend's TCP peer is the proxy itself. Without extra metadata, the application no longer sees the original client as the direct source.
For example, the internal connection chain may look like:
↓ request
Nginx: 10.42.7.10
↓ request
Backend: 10.42.7.50
With no forwarding metadata, application logs may record the following as the client address:
That address belongs to Nginx in the example, not to the end user.
HTTP deployments commonly propagate the original connection context through headers such as:
X-Real-IP
X-Forwarded-Proto
Host
One typical Nginx header block is:
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;
Do not accept forwarding headers blindly from the public Internet. Configure the application or framework to honor them only when the immediate sender is a proxy you control; otherwise a client can forge values such as X-Forwarded-For.
Nginx Reverse Proxy: A Minimal Working Configuration
Assume the application listens only on the local interface:
Nginx will become the public HTTP listener and receive requests on port 80.
First, install the package on an Ubuntu or Debian host:
sudo apt install nginx -y
Then define a server block that forwards traffic to the local process:
listen 80;
server_name portal.edge-lab.example;
location / {
proxy_pass http://127.0.0.1:3200;
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;
}
}
Validate the configuration before reloading the service:
If Nginx reports no syntax errors, apply the change:
The browser now reaches Nginx rather than port 3200. The application can stay bound to a private/local address while Nginx publishes it through the normal web port.
Turn an Nginx Upstream into a Backend Pool
For the next step, imagine three copies of the same application running on private addresses:
10.42.7.22:8080
10.42.7.23:8080
Group those destinations in an upstream block and proxy the public location to that group:
server 10.42.7.21:8080;
server 10.42.7.22:8080;
server 10.42.7.23:8080;
}
server {
listen 80;
server_name portal.edge-lab.example;
location / {
proxy_pass http://app_cluster;
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;
}
}
With no alternative scheduling directive, Nginx rotates requests through the listed upstream servers using its default balancing behavior.
The resulting request path is:
↓
Nginx
↓
upstream app_cluster
├── 10.42.7.21:8080
├── 10.42.7.22:8080
└── 10.42.7.23:8080
Use Least Connections Instead of Simple Rotation
Inside the upstream block, enable:
For example, the pool can be written as:
least_conn;
server 10.42.7.21:8080;
server 10.42.7.22:8080;
server 10.42.7.23:8080;
}
Nginx will now prefer the upstream member with fewer active connections rather than only cycling through the list.
Balance Unequal Servers with Upstream Weights
If the nodes have different capacities, give each one a relative weight:
server 10.42.7.21:8080 weight=3;
server 10.42.7.22:8080 weight=2;
server 10.42.7.23:8080 weight=1;
}
The first node will be selected proportionally more often than the lower-weight node.
Weights are useful when a pool contains mixed VPS sizes or when a newly added server has substantially more resources than older instances.
Tune Passive Failure Handling for Nginx Upstreams
Nginx can temporarily avoid an upstream after repeated failures by using parameters such as:
server 10.42.7.21:8080 max_fails=3 fail_timeout=30s;
server 10.42.7.22:8080 max_fails=3 fail_timeout=30s;
server 10.42.7.23:8080 max_fails=3 fail_timeout=30s;
}
These thresholds should be tuned around the normal behavior of the application and network rather than copied blindly.
Very strict thresholds can eject a healthy server because of a short-lived network hiccup; very relaxed thresholds can leave a genuinely broken node in rotation longer than users will tolerate.
Create an HTTP Backend Pool with HAProxy
HAProxy is designed around proxying connections and distributing traffic, and it is commonly deployed for both HTTP and generic TCP services.
Install the package first:
sudo apt install haproxy -y
A small frontend/backend configuration might look like the following:
bind *:80
default_backend app_nodes
backend app_nodes
balance roundrobin
server app_a 10.42.7.21:8080 check
server app_b 10.42.7.22:8080 check
server app_c 10.42.7.23:8080 check
Before restarting, ask HAProxy to validate the configuration file:
If validation succeeds, restart the service to load the new rules:
You can then confirm that the service is active:
Make HAProxy Probe an Application Health Endpoint
If the application exposes a readiness URL such as:
the backend can actively test that endpoint before choosing a server:
balance leastconn
option httpchk GET /health
http-check expect status 200
server app_a 10.42.7.21:8080 check
server app_b 10.42.7.22:8080 check
server app_c 10.42.7.23:8080 check
HAProxy can now keep servers that fail the HTTP probe out of the normal request path until they become healthy again.
Nginx vs. HAProxy: Choose by Edge-Layer Responsibilities
Both products overlap considerably, yet they are often chosen for different reasons. Nginx combines gateway functions with web-server features, while HAProxy is centered more directly on proxying and traffic distribution.
| Criterion | Nginx | HAProxy |
|---|---|---|
| Reverse proxy | A common role | Available |
| HTTP load balancing | Yes | Yes |
| TCP load balancing | Available through the relevant modules/configuration | A core workload |
| Static files | Can deliver static assets itself | Normally placed in front of a separate web/application server |
| HTTP caching | Built-in web caching options | Not usually the main reason to choose it |
| Typical positioning | Web serving, reverse proxying, and HTTP balancing in one stack | Dedicated connection proxying and backend selection |
Nginx is attractive when the same host should terminate HTTPS, serve files, perform redirects, and proxy dynamic requests without adding another web server to the edge tier.
HAProxy is a strong candidate when the edge is primarily a traffic-management component and detailed HTTP/TCP balancing behavior matters more than serving website content directly.
Where Traefik Fits Best
Traefik is most compelling when backend membership changes frequently and routing data can be discovered from the orchestration environment.
Docker services, for example, may be recreated, scaled up, scaled down, or moved without preserving the same container addresses.
A static Nginx upstream is often written as an explicit list of backend names or addresses:
server 10.42.7.22:8080;
server 10.42.7.23:8080;
Traefik can read supported providers and derive routes or service endpoints automatically, reducing the need to edit a static file for every container change.
That makes it a frequent choice for:
- Docker;
- container-first deployments;
- microservice environments with changing service membership;
- routing that must follow frequently changing backends;
- automatic certificate and HTTPS workflows.
For one conventional website with a stable backend, the automation benefits may not justify extra moving parts, and Nginx can be easier to reason about.
Where Caddy Is a Practical Alternative
Caddy is appealing when you want a concise web/reverse-proxy configuration and prefer the server to automate much of the certificate workflow.
For small deployments, the resulting configuration can be compact enough to reduce day-to-day maintenance.
It can be a good fit for:
- small sites and web applications;
- labs, home servers, and staging environments;
- APIs;
- projects that want HTTPS with minimal certificate plumbing.
Once the system grows, select the edge technology around routing, discovery, observability, protocol support, and failure requirements—not simply around which sample configuration is shortest.
Split the Edge and Application Tiers Across VPS Instances
Growth does not have to mean repeatedly replacing one VPS with a larger one. Another path is to give the proxy and application tier separate machines and scale them independently.
One possible split is:
VPS 1
VPS 2
VPS 3
VPS 4
The entry node is responsible for client-facing connections, while application processing is spread across dedicated backend machines.
With Falconcloud, each virtual server can be sized around a specific responsibility. The proxy may need network headroom while application nodes need more CPU or RAM, and those groups can be expanded independently instead of moving the entire stack to one oversized VM.
A simple migration path is to keep the first VPS, introduce a second and third application node, and add those addresses to the Nginx or HAProxy backend pool as demand increases.
Deploy a multi-VPS setup in Falconcloud and add proxy or backend capacity as real traffic demands it.
Should the Load Balancer Have Its Own VPS?
No—especially at the beginning.
For a small workload, Nginx and the application can share the same virtual machine:
Nginx
Application
Database
This keeps the topology easy to operate and avoids paying for an extra VM before it provides a real benefit.
Once the application tier expands to multiple hosts:
Proxy VPS
separating the public gateway from the application machines becomes easier to justify.
A dedicated edge node can then:
- own certificates and TLS policy in one place;
- leave application servers on private or restricted networks;
- expand the backend pool without changing the address clients use;
- scale edge capacity separately from application compute;
- make firewall rules between the gateway and backend tier more explicit.
The separation also exposes another design question: if every request enters through one balancer, what happens when that balancer fails?
One Load Balancer Can Still Be the Failure Point
Consider a service with redundant application nodes but only one entry process:
The backend tier has redundancy, yet the front door does not.
Losing one application node can be absorbed by the remaining members of the pool.
Losing the only balancer is different: healthy backends still exist, but clients no longer have a route to them.
The topology therefore still contains a single point whose failure can make the whole service unreachable:
SPOF
Load Balancer
High availability requires redundancy at the gateway tier as well as behind it.
Make the Entry Layer Redundant
A common pattern is to operate at least two edge nodes and provide a mechanism that directs traffic to whichever one is available:
VIP
LB 1
LB 2
Backend Pool
When one edge node becomes unavailable, the other can continue serving the public endpoint.
The handoff mechanism depends on the platform and may involve:
- a movable virtual IP address;
- VRRP/Keepalived on compatible networks;
- DNS-based failover or distribution;
- a provider-managed load-balancing service;
- another redundant network tier in front of the proxies.
Replicating only the application tier is not enough to claim end-to-end high availability.
Review every dependency in the request path, for example:
↓
Load Balancer
↓
Application
↓
Database
↓
Storage
Any indispensable component that has no alternate path can still interrupt the service.
Does Database Traffic Use the Same Balancing Pattern?
Usually not with the same simple model used for interchangeable web replicas.
A stateless web tier can often be represented as several equivalent instances:
App 2
App 3
because any member can process essentially the same class of request.
A database carries durable state, so routing decisions are tied to replication topology, write ownership, failover, and consistency guarantees.
A naive pool such as:
↓
PostgreSQL 1
PostgreSQL 2
can be unsafe if the balancer does not understand which node is writable, which is read-only, and how failover is coordinated.
A database routing design generally has to answer questions such as:
- where write traffic is allowed;
- whether reads can be sent to replicas;
- what replication model keeps nodes synchronized;
- how loss of the current primary is detected;
- which component promotes or selects a replacement;
- how clients discover or connect to the new writable endpoint.
Treat database routing as its own high-availability problem rather than reusing an HTTP upstream pattern without understanding the database's replication semantics.
Publish Several Sites Through One Reverse Proxy
One proxy can inspect the requested host and map different domains to different local applications.
A simple routing plan might be:
Nginx
127.0.0.1:3301
127.0.0.1:3302
127.0.0.1:9100
Externally, visitors use the normal HTTP/HTTPS ports; internally, each service can keep its own private listener.
That layout provides several practical benefits:
- only the proxy needs public web ports;
- certificates can be handled centrally;
- one public IP can represent several hostnames;
- edge access logging is collected in one place;
- internal ports can change without altering the URL users visit.
Reverse Proxying Services Inside a Docker Network
A common container layout looks like this:
Nginx / Traefik
frontend
backend
admin
The host therefore needs to publish only the proxy's web ports:
443:443
Application containers can stay reachable only by other containers and the proxy on the internal network.
The result is a smaller public attack surface and one place to control incoming HTTP routing, TLS, and logs.
Avoid building production routing around container IP addresses that are expected to change when containers are recreated. Prefer stable service names, Docker-network DNS, orchestration discovery, or another repeatable way to resolve backends.
Balancing Multiple Container Replicas
Assume the same backend service has been started as several replicas:
backend-2
backend-3
The edge layer can treat those replicas as one pool:
When replicas are created and removed dynamically, backend discovery becomes part of the balancing design.
Manual edits for every scale event turn an otherwise dynamic container platform into a static operational process.
Container-oriented gateways therefore commonly integrate with service discovery or orchestration metadata.
From One Application Instance to a Horizontal Pool
A project may begin with a single application process:
↓
App 1
When that instance approaches its comfortable capacity, another replica can be introduced:
├── App 1
└── App 2
The pool can continue expanding:
├── App 1
├── App 2
└── App 3
Increasing the number of peer instances is horizontal, or scale-out, growth.
Vertical scaling follows a different strategy:
2 CPU / 4 GB RAM
4 CPU / 8 GB RAM
8 CPU / 16 GB RAM
Neither method is universally better; both have a place.
Scaling up is operationally simple, but a single machine can only be enlarged so far and still remains one failure domain.
Scaling out requires routing, health checks, shared state decisions, and more operational tooling, but capacity can grow by adding replicas instead of repeatedly replacing one host.
The load balancer is what turns those replicas into one service from the client's perspective.
A Practical Layout for One Small Website
When there is only one application host, there is no pool to balance, so a dedicated load-balancing tier normally adds complexity without adding capacity.
A compact and useful arrangement is:
↓
Nginx
↓
Application
Nginx can sit in front of the app and handle edge tasks such as:
- terminating HTTPS;
- redirecting plaintext HTTP;
- serving static assets when appropriate;
- forwarding dynamic requests to the local app;
- writing centralized access and error logs.
A load balancer becomes valuable when it has real alternatives to choose from; placing one in front of a single target merely for architectural appearance rarely helps.
Architecture for Several Copies of the Same Application
Once the same service runs on multiple machines:
App 2
App 3
the public entry layer must choose one healthy instance for each new request or connection.
A common pattern is:
↓
Nginx / HAProxy
↓
Application Pool
At this point, the gateway is performing actual load distribution rather than simple one-to-one proxying.
Layer 7 is generally the most flexible option for ordinary HTTP workloads.
For generic TCP/UDP services—or when the edge should not parse HTTP—Layer 4 may be a better fit.
Reverse Proxy and Load Balancing Patterns for APIs
If the API has only one running instance:
↓
Nginx
↓
API
a reverse proxy can provide the public hostname, HTTPS, headers, logging, and private upstream routing without a separate balancing tier.
If the API is replicated across several instances:
↓
Load Balancer
↓
├── API 1
├── API 2
└── API 3
place those instances in a health-checked pool so failed nodes stop receiving new requests.
Stateless API design makes this much easier: any healthy replica can serve the next request without depending on process-local session data.
Routing and Balancing in a Microservice Architecture
A microservice system can expose many independently deployed services behind one external API surface.
At the edge, routing might first look like:
Reverse Proxy / Gateway
User Service
Order Service
Payment Service
Catalog Service
Each routed service can then be a pool of its own rather than a single process:
/users
The edge therefore makes two decisions: which service owns the request, and which healthy replica of that service should receive it.
At larger scale, this may be complemented by components such as:
- API Gateway;
- an ingress controller;
- a service mesh for east-west traffic;
- internal service-to-service load balancing;
- a service registry or discovery mechanism.
Reverse Proxy, Load Balancer, and API Gateway: Related but Different
They overlap, but the names describe different primary responsibilities.
A reverse proxy is the controlled hop that publishes and routes backend services.
A load balancer chooses among several interchangeable targets for the same logical service.
An API gateway usually layers API-specific policy and product features on top of basic proxying, for example:
- central authentication or token validation;
- per-client or per-route rate policy;
- API key enforcement;
- method-aware routing rules;
- usage quotas;
- request/response transformation;
- centralized API access policy and governance.
Product boundaries are not strict: one gateway may expose features from all three categories.
Reverse Proxy vs. CDN: Why They Solve Different Problems
A proxy next to the origin controls how traffic enters your infrastructure; a CDN adds geographically distributed edge capacity closer to users.
The origin-side reverse proxy can:
↓
Nginx
↓
Application
A CDN adds another layer of cache/edge locations outside the origin network:
↓
CDN Edge
↓
Reverse Proxy
↓
Application
That distributed layer is useful for tasks such as:
- serving cacheable assets from edge locations;
- offloading repeat requests before they reach the origin;
- reducing network distance for users in different regions;
- applying provider-specific filtering or edge-security features where available.
Even with a CDN in front, the origin still needs a sensible proxy, firewall, routing, and backend design.
Keep Backend Application Ports Private
When the reverse proxy is the only supported public entry path, exposing the application's own listener to every Internet host is unnecessary.
A safer network path is:
↓
Public IP
↓
Reverse Proxy
↓
Private Network
↓
Backend Servers
The backend firewall can restrict the application port to the proxy's address, subnet, or another trusted internal network.
Conceptually, the policy is:
Proxy → Backend:8080 = ALLOW
Fewer public listeners mean fewer places where external traffic can bypass the controls implemented at the proxy.
Plan Public and Administrative Ports Separately
A normal public HTTP service generally exposes only the ports required by visitors:
TCP 443
SSH:
does not have to accept connections from arbitrary public addresses.
Administrative access is better limited to trusted source addresses, a VPN, a bastion host, or another controlled management path.
Private application/database ports—for example:
8000
8080
9000
should remain internal when only the proxy or other infrastructure components need to reach them.
Use the Reverse Proxy as an Early Rate-Control Point
Because every public request already crosses the reverse proxy, it is a natural place to apply simple per-client or per-route request limits.
A login endpoint, for example, can be given a lower request rate than ordinary static content.
Rate controls are often useful around:
- authentication endpoints;
- signup flows;
- APIs;
- CPU- or database-expensive search operations;
- absorbing basic bursts before they consume application resources.
Local Nginx limits are only one control. They do not replace upstream DDoS mitigation or a WAF, both of which address threats and traffic patterns that go beyond simple request-rate rules.
Set Timeouts Around Real Application Behavior
An upstream connection needs sensible time boundaries; otherwise stalled backends can hold proxy resources for an excessive period.
The exact directive names vary, but the design usually distinguishes between several stages:
- connect timeout — the maximum time allowed to establish the upstream connection;
- read timeout — how long the proxy tolerates waiting for upstream data;
- send timeout — limits around transferring the request or response stream;
- idle timeout — how long an otherwise open but inactive connection can remain allocated.
An Nginx configuration might include values such as:
proxy_send_timeout 60s;
proxy_read_timeout 60s;
Treat example numbers as placeholders, not universal recommendations.
An application that legitimately waits on multi-minute work will fail behind an overly short read timeout even when nothing is wrong.
Conversely, if healthy API responses normally arrive in a fraction of a second, allowing a stuck upstream to occupy a connection for many minutes may simply hide a failure.
WebSocket and Long-Lived Connections Change the Balancing Model
HTTP request/response traffic is not always short-lived. Chat systems, live dashboards, notification channels, multiplayer backends, and streaming-style APIs may hold connections open for long periods. Those workloads put different pressure on proxy timeouts, connection limits, and balancing logic.
A short request typically opens, exchanges data, and finishes quickly:
A WebSocket begins with HTTP but then keeps the upgraded connection alive:
↓
Proxy / Load Balancer
↓
Backend
↕
Long-lived connection
That difference matters for balancing. A node that receives few new HTTP requests may still be carrying thousands of long-lived sockets. Counting only new requests can therefore give a misleading picture of how busy each backend really is.
For Nginx to proxy a WebSocket upgrade, the relevant upgrade headers must be forwarded correctly:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
`proxy_read_timeout` also becomes important. If legitimate WebSocket sessions can remain quiet longer than this value, Nginx may close a healthy connection simply because no data crossed it during the timeout window.
Before scaling this type of service, verify:
- that the chosen edge product correctly supports connection upgrades;
- the sustainable concurrent-connection count per backend;
- how idle/read timeouts interact with the protocol;
- whether established sessions require backend affinity;
- how clients reconnect when a node is restarted or drained;
- how the remaining pool absorbs new connections after a node is removed.
For a REST API, requests per second and latency may dominate capacity planning. For WebSocket workloads, concurrent sockets, per-connection memory, connection lifetime, and reconnect storms can matter just as much. The balancing strategy should follow the actual workload rather than being copied unchanged from a short-request API.
Monitoring the Edge Layer: Signals That Matter
A running proxy process proves only that the daemon is alive. It does not prove that upstreams are healthy, latency is acceptable, certificates are valid, or users are receiving successful responses.
Useful edge-layer metrics include:
- request/connection volume;
- response status distribution;
- 4xx and 5xx rates;
- end-to-end and upstream latency;
- active and waiting connections;
- upstream connect/reset/timeout errors;
- health state for every backend member;
- CPU, memory, and file-descriptor pressure on the edge node;
- network throughput and saturation;
- certificate expiry dates and renewal status.
When measuring latency, separate the stages where possible:
and
Backend response time
Without that split, a slow response is hard to attribute to the proxy, network, application, or downstream dependency.
Logging: Capture Enough Context to Debug Upstream Failures
Keep both request logs and proxy/error logs at the entry layer; they show what clients asked for and what happened when the proxy contacted the upstream.
A useful access-log format normally records fields such as:
- timestamp;
- method;
- the path or URL;
- returned status;
- response bytes;
- total request duration;
- chosen upstream address/server name;
- upstream status and timing information.
With that context, you can diagnose cases such as:
↓
Which backend did the proxy select?
↓
Did the backend respond?
↓
Was it a timeout or connection refused?
Without those records, intermittent gateway errors become much harder to reproduce and attribute.
502 Bad Gateway: The Proxy Could Not Use the Upstream Response
A 502 usually points to a failure between the proxy and the configured upstream: the connection may fail, reset, or return something the proxy cannot treat as a valid response.
A typical failure path is:
Nginx
✕ Backend unavailable
Start troubleshooting at the proxy-to-backend boundary and verify:
- that the application process/service is actually running;
- that `proxy_pass` points to the intended address and port;
- that the proxy host can route to and connect to the backend;
- that host or network firewall rules permit the traffic;
- that a remote backend is not accidentally bound only to its own loopback interface;
- that proxy and application logs do not show startup, reset, protocol, or dependency errors.
From the proxy host, test the upstream directly, for example:
If a direct request from the proxy host fails, changing browser settings will not solve the issue—the break is on the internal upstream path.
504 Gateway Timeout: The Upstream Took Too Long
A 504 generally indicates that the gateway waited for the upstream beyond the configured timeout window. The backend path existed, but the expected response did not arrive soon enough.
The resulting request path is:
Common causes include:
- a database query that runs much longer than expected;
- CPU, memory, thread, or worker saturation on the application;
- a downstream API or integration that is stalled;
- a proxy timeout that does not match legitimate request duration;
- insufficient compute or memory resources;
- synchronous work that should perhaps be moved to a background job.
Do not treat a huge timeout as the default fix. Measure where the time is being spent first; otherwise a larger value may only make users wait longer for an already unhealthy backend.
Frequent Proxy and Load-Balancing Design Mistakes
Leaving an Internal Backend Publicly Reachable
If all supported traffic is supposed to pass through the edge layer, a public rule for the application port creates a bypass around that design.
Restrict the backend listener with host and network firewall policy.
Losing the Original Client Address
The application then logs the proxy address for every request, making abuse analysis and client-aware logging unreliable.
Forward the required metadata and configure the application to trust it only from known proxies.
Sending Traffic Without Backend Health Signals
A broken instance can remain in rotation because the edge has no reliable signal that it should be removed.
Users then experience intermittent failures depending on which backend they are assigned.
Keeping User Sessions Only in Process Memory
Once requests begin moving between replicas, the next server may not know about a session created on the previous one.
Affinity can reduce the symptom, but shared external session storage is usually the more scalable design.
Treating One Load Balancer as High Availability
Backend redundancy cannot compensate for losing the only path through which clients can reach those backends.
Add redundancy or a managed failover mechanism at the entry tier when availability requirements justify it.
Balancing Unequal Nodes as If They Were Identical
A simple rotation can overload a smaller node while larger instances still have spare capacity.
Use relative weights, connection-aware scheduling, or another strategy that reflects real capacity.
Allowing Stalled Upstreams to Hold Connections Too Long
Excessive waits consume sockets/workers and can turn a backend failure into a larger resource problem at the proxy.
Cutting Off Legitimate Long Requests
Valid operations that legitimately need more time are terminated as if they were failures.
Ignoring Gateway and Server Error Rates
Processes can remain healthy enough to stay up while a meaningful fraction of requests still return 5xx responses.
Error-rate monitoring surfaces that degradation before support tickets become the first alerting mechanism.
A Simple Decision Path for the Entry Layer
Choose the smallest architecture that solves the current routing and scaling problem.
With a single backend, ask whether you need an edge server to:
- publish a domain name;
- terminate HTTPS;
- keep the application's private port off the public Internet;
- apply host/path routing;
If so, a reverse proxy already solves the problem.
The resulting request path is:
When the same service exists as several equivalent instances:
App 2
App 3
introduce backend selection in the entry layer:
If the gateway must route requests and also select among replicas:
↓
Reverse Proxy + Load Balancer
↓
Application Pool
choose software that can combine reverse-proxy rules and load-balancing logic in the same deployment.
Recommended Starting Point by Project Type
| Project situation | Reasonable first choice | Rationale |
|---|---|---|
| Single website on one VPS | Nginx as the reverse proxy | Publishes HTTPS and the application cleanly without inventing a backend pool |
| Several applications sharing one host | Reverse proxy | Maps hostnames/paths to separate local services |
| Several replicas of one web service | Layer 7 balancing | Understands HTTP and can keep unhealthy replicas out of rotation |
| Non-HTTP TCP service | Layer 4 balancing | Distributes connections without needing HTTP-aware rules |
| Docker environment with changing services | Discovery-aware proxy such as Traefik | Routes can follow changing container/service membership |
| Replicated API service | Nginx or HAProxy with a health-checked pool | One edge tier can publish the API and distribute traffic |
| Critical service with high availability requirements | Redundant entry nodes and redundant backends | Availability depends on removing failure points across the whole request path |
Pre-Launch Checklist for the Proxy/Balancing Layer
Before sending production traffic through the new entry layer, verify:
Conclusion: Start with the Role, Then Choose the Tool
Reverse proxying and load balancing solve adjacent problems, and mature edge software often provides both.
Use reverse-proxy functionality when you need a controlled front door for the application—one place for HTTPS, host/path routing, forwarded headers, caching, logging, or hiding internal listeners behind a public address.
Add balancing when a single logical service is backed by more than one equivalent instance and the edge must decide which healthy member receives new work.
A small deployment can stay intentionally simple:
Once the application is replicated, the topology can evolve to:
Nginx / HAProxy
Most websites and HTTP APIs benefit from L7 because routing can use application context. L4 remains useful for transport-oriented services and cases where the edge should forward connections without inspecting HTTP.
Nginx is versatile when web-server and proxy responsibilities live together; HAProxy is purpose-built around traffic proxying and balancing; Traefik is especially useful when routes and backends should follow a dynamic container environment.
Avoid designing for complexity as an end in itself. A single VPS with a reverse proxy is a perfectly valid starting point; add backend pools, redundant balancers, and more elaborate routing only when traffic, availability, or operational requirements make them useful.
Grow the Proxy and Backend Topology with Falconcloud
The topology can be expanded in stages. Begin with Nginx and the application together, separate the public gateway when needed, and add application VPS instances behind it as demand increases.
With Falconcloud, cloud servers can be assigned distinct roles such as frontend delivery, reverse proxying, load balancing, application processing, databases, or supporting services. You control the software stack on each node and can reshape the topology as the project moves from a single VPS to a distributed architecture.
One possible Falconcloud layout is:
Falconcloud VPS 1
VPS 2
VPS 3
VPS 4
Keep the first version small, measure it, and introduce additional nodes when capacity or availability requirements justify the change.
Create a VPS in falconcloud.ae and build an entry layer that can grow from simple reverse proxying to a multi-backend load-balanced service.
Proxy and Load Balancer FAQ
How Do a Reverse Proxy and a Load Balancer Differ?
A reverse proxy is the controlled gateway that publishes and routes backend services, while a load balancer decides which member of a replicated backend pool should receive traffic. Because those jobs are adjacent, software such as Nginx or HAProxy can provide both in one edge deployment.
Is a Load Balancer Useful with Only One Application Server?
Usually there is nothing to distribute when only one application instance exists. A reverse proxy can still add HTTPS, routing, headers, and private upstream ports; load balancing starts to provide value after a second equivalent backend is introduced.
Should I Use Nginx or HAProxy for Load Balancing?
Nginx is convenient when the edge also needs web-server features such as static delivery, redirects, and general HTTP handling. HAProxy concentrates more directly on HTTP/TCP proxying and backend-pool behavior. Choose based on the responsibilities of your gateway rather than treating one product as universally superior.
When Should I Choose L4 Instead of L7?
L7 is usually the flexible choice for HTTP because routing can use hostnames, paths, headers, methods, or cookies. L4 is appropriate when the edge only needs transport-level information or the service is not HTTP at all.
What Should Happen When a Backend Fails?
A health-checked pool should stop assigning new work to the failed instance and continue with healthy members. For critical services, remember that backend redundancy is only part of the availability story—the public balancing layer also needs an appropriate failover design.
Can the Reverse Proxy Share a VPS with the Application?
Yes. A small deployment can expose only Nginx on ports 80/443 while the application listens on an internal port on the same host. If the system grows, the gateway can later move to its own VPS and the application can be replicated across several backend nodes.