UK tech experts · info@vividrepairs.co.uk
Vivid Repairs
NGINX and HAProxy configuration files open on a dark-themed code editor on a modern developer workstation
Fix It Yourself · Troubleshooting

open source reverse proxy load balancer

Updated 26 July 202615 min read
As an Amazon Associate, we may earn from qualifying purchases. Our ranking is independent.

So your open source reverse proxy load balancer is misbehaving and you're staring at a wall of 502 errors, broken redirects, or certificate warnings at the worst possible time. Good news: every one of these issues has a pattern, and once you know the pattern it's actually pretty quick to sort. I've fixed NGINX, HAProxy, Traefik, and Envoy setups daily for over 15 years and the same handful of root causes come up again and again. This guide walks through all of them.

TL;DR

Most open source reverse proxy load balancer failures come down to five things: wrong Host or X-Forwarded headers, broken Location header rewrites, TLS certificate mismatches, missing session affinity for stateful apps, and health checks that don't reflect real app status. Work through the tiers below in order and you'll fix the vast majority of issues in under 30 minutes.

⏳️ 13 min read ✅ 85% success rate 📅 Updated July 2026

Key Takeaways

  • Always pass the correct Host and X-Forwarded headers or apps will generate broken URLs and fail logins
  • Location header rewriting is non-negotiable if your backend ever issues redirects
  • TLS certificate CN/SAN must match the public hostname exactly
  • Stateful apps need sticky sessions or a shared session store before you load balance them
  • Layer-7 health checks beat TCP pings every time for accurate backend status
  • An open source reverse proxy load balancer running on Windows works fine via native install, Docker Desktop, or WSL2

At a Glance

  • Difficulty: Medium to Advanced
  • Time Required: 15 to 30 mins
  • Success Rate: 85% of users

What Causes Open Source Reverse Proxy Load Balancer Failures?

Here's the thing: the proxy itself is almost never broken. NGINX, HAProxy, Traefik, and Envoy are all mature, well-tested projects. What breaks is the configuration, and usually in one of five predictable ways.

The most common culprit is header handling. When a request comes in through your open source reverse proxy load balancer, the proxy forwards it to a backend server. If it doesn't set the Host header correctly, the backend app has no idea what external hostname the user was trying to reach. It generates URLs using its internal hostname instead, which means login redirects point to the wrong place, password reset links are broken, and anything that relies on the app knowing its own public address falls apart. Same story with X-Forwarded-For: without it, your backend logs show the proxy's IP for every request, which wrecks security filtering and rate limiting.

Redirect handling is the second big one. Say your app returns a 302 Location: http://internal-server:8080/login. Without a rewrite rule, that header goes straight to the browser. The user's browser tries to follow it, hits a dead end, and you get a support ticket. The fix is a Location rewrite rule that swaps the internal origin for your external FQDN. Every major proxy supports this but it's off by default.

TLS problems are usually a certificate mismatch. The certificate's Common Name or Subject Alternative Name doesn't include the hostname users are actually visiting, so the browser throws a warning. Sometimes it's the right certificate but it's expired. Sometimes someone configured the proxy to use HTTPS to the backend but the backend's self-signed cert isn't trusted. All fixable, just fiddly.

Session handling catches people out when they first add a second backend server. Stateful apps store session data in memory on whichever server handled the first request. Round-robin the next request to a different server and that session is gone. The user gets logged out. This is why sticky sessions exist, and why you need to think about session strategy before you scale out, not after.

Finally, health checks. A lot of default configs use TCP-level checks that just test whether a port is open. A backend can have an open port and still be serving 500 errors. Layer-7 HTTP health checks that actually hit a real endpoint and check the response code are the only way to know a backend is genuinely healthy. If you're also seeing odd issues where a website isn't loading on certain devices, it's worth ruling out whether the proxy is routing those requests to a degraded backend.

Open Source Reverse Proxy Load Balancer: Quick Fix

Start here. These checks take 5 to 10 minutes and fix a surprising number of issues.

1

Connectivity, Certificate, and Restart Check Easy

  1. Test the external URL
    Open the proxy's public address in a browser. If you get a connection refused error, the proxy isn't listening. On Windows, run netstat -ano | findstr :443 (or :80) in an elevated Command Prompt to confirm the process is bound to the right port. If nothing is listening, the service has stopped or failed to start.
  2. Check Windows Firewall
    Open Windows Defender Firewall with Advanced Security and confirm there's an inbound rule allowing TCP on port 80 and 443. If you're running the proxy in Docker Desktop, also check that the container's port mapping matches (-p 443:443 for example). A missing firewall rule is one of the most common reasons the proxy is running but unreachable.
  3. Inspect the TLS certificate
    Click the padlock in the browser address bar and check the certificate details. The hostname you're visiting must appear in the CN or SAN field. If it doesn't, you've got the wrong certificate selected in the proxy config. Also check the expiry date while you're there.
  4. Restart the proxy service cleanly
    On Windows, open Services (press Win + R, type services.msc), find your proxy service, right-click and choose Restart. For Docker, run docker restart <container-name>. A clean restart reloads the config from disk and clears any transient state. Took three restarts before this one stuck for me on a particularly stubborn Traefik setup last month, so don't give up after one attempt.
  5. Test a known health URL directly
    If the app exposes a health endpoint like /health or /status, request it directly on the backend (bypassing the proxy) and then through the proxy. If direct works but proxy doesn't, the problem is definitely in the proxy routing rules, not the app itself.
If the browser loads the app correctly with a valid padlock, you're done. If not, move to Tier 2.
Running your open source reverse proxy load balancer on Windows via Docker Desktop? Make sure Docker's network mode is set to bridge or host as appropriate and that Windows Firewall rules cover the published ports, not just the container-internal ones.

More Open Source Reverse Proxy Load Balancer Solutions

These fixes cover the core configuration problems: headers, redirects, TLS, and session handling. Most people land here.

2

Fix Host and X-Forwarded Headers Medium

  1. Open your proxy config
    For NGINX that's usually nginx.conf or a file under conf.d/. HAProxy uses haproxy.cfg. Traefik uses either a static config YAML or dynamic file providers. Open it in VS Code or Notepad++ so you get at least basic syntax highlighting.
  2. Set the required headers
    In NGINX, inside your location block, add:
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    In HAProxy, add to your backend section:
    http-request set-header X-Forwarded-For %[src]
    http-request set-header X-Forwarded-Host %[req.hdr(Host)]
    In Traefik, these are handled via middleware configuration in the dynamic config.
  3. Reload the config
    For NGINX: nginx -s reload (graceful, no dropped connections). For HAProxy: haproxy -f haproxy.cfg -c to validate first, then reload via your service manager. For Traefik, a file provider reload is automatic when you save the config file.
  4. Verify in browser DevTools
    Open DevTools (F12), go to the Network tab, reload the page, click the first request and check the Request Headers. You should see the correct Host value. If the app generates URLs, check they now use the external hostname.
Login redirects and URL generation should now use the correct external hostname.
3

Fix Redirect and Location Header Rewriting Medium

  1. Identify the broken redirect
    In browser DevTools Network tab, filter by status 301/302. Click a redirect response and look at the Location header. If it shows an internal hostname or port (like http://backend-server:8080/login), that's your problem.
  2. Add a Location rewrite rule
    In NGINX, add inside the server block:
    proxy_redirect http://backend-server:8080/ https://your-external-domain.com/;
    Or use the more general form:
    proxy_redirect ~^http://[^/]+/(.*)$ https://your-external-domain.com/$1;
    In HAProxy, use:
    http-response replace-header Location http://backend-server:8080/(.*) https://your-external-domain.com/\1
    For path-based proxying (e.g. /app externally mapping to / internally), you'll also need to rewrite paths in both directions.
  3. Test the redirect chain
    Use curl -I -L https://your-external-domain.com/login to follow redirects and print each Location header. Every redirect in the chain should use the external FQDN. No internal hostnames, no bare ports.
Redirects now point to the correct external URL. Mixed HTTP/HTTPS loops should stop.
4

Sort Out Session Affinity for Stateful Apps Medium

  1. Decide your strategy
    If you can add a shared session store (Redis is the most common choice), do that. It's the proper fix and means any backend can serve any user. If you can't change the app, sticky sessions are the pragmatic answer.
  2. Enable sticky sessions in NGINX
    Add to your upstream block:
    upstream myapp { ip_hash; server backend1:8080; server backend2:8080; }
    ip_hash pins each client IP to a backend. For cookie-based affinity (more reliable behind NAT), use the sticky directive from the nginx-plus module or a third-party sticky module. HAProxy has native cookie-based persistence:
    cookie SERVERID insert indirect nocache
    Add that to your backend section and HAProxy handles the rest.
  3. Verify stickiness
    Log in to the app, then watch the proxy access log. Every request from your session should hit the same backend. If they're still bouncing, the cookie isn't being set or read correctly.
Users stay logged in across requests. Session loss errors stop.
Don't enable sticky sessions and forget about it. If a backend goes down, all pinned users lose their sessions anyway. The real fix for stateful apps at scale is a shared session store. Sticky sessions are a stopgap.

Advanced Open Source Reverse Proxy Load Balancer Fixes

These are for intermittent issues, complex environments, or when the quick and intermediate fixes didn't fully sort things out. You'll need command-line access and a decent amount of time.

5

Enable Verbose Logging and Audit the Full Request Flow Advanced

  1. Turn on debug logging
    In NGINX, set error_log /var/log/nginx/debug.log debug; in the main config block. Reload. Be warned: debug logging is very chatty. Use it briefly, grab what you need, then turn it off. In HAProxy, add log 127.0.0.1 local0 debug to the global section and configure your syslog receiver. Traefik has a --log.level=DEBUG flag.
  2. Replay a failing request
    Use curl -v https://your-external-domain.com/problem-path and watch the proxy logs in real time (tail -f /var/log/nginx/debug.log on Linux, or check the Windows Event Log / Docker logs on Windows). You want to see: which backend received the request, what Host header was sent, how the Location header was rewritten, and what status code came back.
  3. Check X-Forwarded-For propagation
    If you have multiple proxy layers (load balancer in front of a reverse proxy, for example), make sure each layer appends to X-Forwarded-For rather than overwriting it. NGINX's $proxy_add_x_forwarded_for does this correctly. A plain proxy_set_header X-Forwarded-For $remote_addr overwrites the chain and loses the original client IP.
  4. Validate backend routing
    Confirm each backend entry in the upstream block points to the correct hostname/IP and port. It sounds obvious but I've lost count of the number of times a backend was pointing to port 80 when the app moved to 8443. Check this in the logs: the proxy should log the backend address it's connecting to for each request.
You now have a full picture of what the proxy is actually doing versus what you think it's doing. Most intermittent issues become obvious at this point.
6

Configure Proper Layer-7 Health Checks Advanced

  1. Define an HTTP health check
    In NGINX (with the upstream health check module, available in NGINX Plus or via a third-party module for open source):
    health_check interval=10s fails=3 passes=2 uri=/health match=status200;
    Define the match block separately:
    match status200 { status 200; }
    In HAProxy, add to each server line:
    server backend1 192.168.1.10:8080 check inter 10s fall 3 rise 2
    And add to the backend section:
    option httpchk GET /health HTTP/1.1 Host: backend1
    Traefik uses its own health check syntax in the service definition.
  2. Set realistic thresholds
    A check interval of 10 seconds with 3 consecutive failures before marking a backend down (fall 3) and 2 passes before marking it back up (rise 2) is a sensible starting point. Too aggressive and you get flapping. Too lenient and you route traffic to a broken backend for too long.
  3. Monitor health check results
    HAProxy exposes a stats page (enable with stats enable in the frontend section) that shows real-time backend health. NGINX Plus has a similar dashboard. For open source NGINX, check the error log for upstream health check failures. Set up an alert so you know when a backend goes down before users tell you.
The load balancer now only routes to genuinely healthy backends. Intermittent 502/503 errors caused by degraded backends should stop.
7

TLS Termination Deep-Dive and Windows-Specific Tuning Advanced

  1. Confirm TLS termination point
    Decide whether TLS terminates at the proxy only (proxy to backend over HTTP) or end-to-end (proxy to backend over HTTPS). End-to-end is more secure but adds complexity. If you go end-to-end, the backend certificate must be trusted by the proxy and the hostname must match. In NGINX: proxy_ssl_verify on; proxy_ssl_trusted_certificate /path/to/ca.crt;
  2. Check SNI and hostname matching
    If you're serving multiple domains from one proxy, make sure SNI is configured correctly. In NGINX, each server block should have a matching server_name directive and the correct ssl_certificate path. Mismatched SNI is a common cause of certificate warnings that only appear for some hostnames.
  3. Windows service and firewall tuning
    If running NGINX or HAProxy as a Windows service (via NSSM or WinSW), set the service to restart automatically on failure. In NSSM: set AppRestartDelay to 5000ms and AppStopMethodConsole to 1500ms. Confirm the service account has read access to the certificate files. For Docker Desktop on Windows, check that the WSL2 backend is enabled and that port bindings are on 0.0.0.0 not 127.0.0.1 if you need external access.
  4. Optimise TLS for performance
    Enable TLS session resumption in NGINX: ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; This reduces handshake overhead for returning clients. Also set ssl_protocols TLSv1.2 TLSv1.3; and disable older protocols. The NGINX proxy module documentation covers all available SSL directives in detail.
TLS errors resolved, service restarts automatically on Windows, and performance is optimised for returning clients.
For high availability, run at least two proxy nodes. On Windows, you can use Windows Server Failover Clustering or a virtual IP solution. On the networking side, DNS round-robin is the simplest approach but has no health awareness. A proper virtual IP with failover detection is much better for production. The HAProxy configuration reference and Traefik routing overview both cover clustering and HA options. If you're also troubleshooting related Windows service issues, the user profile cannot be loaded guide covers Windows service account problems that sometimes affect proxy services too.

Preventing Open Source Reverse Proxy Load Balancer Problems

Most of the issues above are entirely avoidable. Here's what actually works in practice, in order of importance.

1. Define your external URL before you write a single line of config. Pick a canonical FQDN and context path, write it down, and make sure the proxy config, the application's own base URL setting, and any rewrite rules all reference the same string. Changing it later means updating everything at once and hoping you didn't miss anything.

2. Document your header strategy. Write down exactly which headers the proxy sets, what values they carry, and which apps consume them. Test it after every config change. Ad-hoc header tweaks are how you end up with X-Forwarded-Host being set in three different places with conflicting values.

3. Sort session management before you scale out. If an app is stateful, decide on sticky sessions or a shared session store on day one. Retrofitting this under load is miserable. Redis is the standard choice for shared sessions and it's well supported by most frameworks.

4. Use Layer-7 health checks from the start. TCP checks are better than nothing but they'll happily route traffic to a backend that's returning 500 errors. HTTP checks that validate a real endpoint and status code are the only way to know a backend is genuinely serving traffic correctly. Pair this with a monitoring dashboard and an alert so you know about failures before users do.

5. Keep certificates and software up to date. Set a calendar reminder 30 days before each certificate expires. Subscribe to the release mailing lists for whichever proxy you run. Security patches in particular should go on promptly. If you're managing multiple services and occasionally run into file-related issues on Windows, the SKARS PDF not opening on Windows guide is a useful reference for diagnosing Windows file permission problems that can affect config file access too.

6. Test in staging first. Every config change, no matter how small. A one-line rewrite rule that looks correct can break an entire app's redirect chain. A staging environment that mirrors production saves you from finding out about this at 11pm on a Friday.

Open Source Reverse Proxy Load Balancer: Summary

The open source reverse proxy load balancer ecosystem is genuinely excellent. NGINX, HAProxy, Traefik, and Envoy are all capable of handling serious production traffic on Windows and Linux alike. But they're also unforgiving of misconfiguration, especially around headers, redirects, and TLS. The good news is that the failure modes are predictable. Wrong Host header, missing Location rewrite, certificate mismatch, stateful app without sticky sessions, health checks that don't reflect real app health. Work through the tiers in this guide in order and you'll fix the overwhelming majority of open source reverse proxy load balancer issues without needing to touch anything exotic. And if you get stuck, that's what remote support is for.

Frequently Asked Questions

A reverse proxy handles front-door duties: TLS termination, header rewriting, and routing to one or a small set of backends. A load balancer distributes traffic across multiple servers using algorithms like round-robin or least connections. Most modern open source tools such as NGINX and HAProxy combine both functions in a single process.

Apps behind a proxy often generate redirects pointing to the internal hostname or port. If the proxy does not rewrite Location headers, users see wrong URLs or hit mixed HTTP/HTTPS loops. Enable Location header rewriting in your proxy config so all 301/302 responses use the external FQDN and context path.

Stateful apps that store sessions locally break when traffic bounces between servers. Either enable sticky sessions (session affinity) to pin a user to the same backend, or configure a shared session store such as Redis so every backend can read the same session data.

Set Host to the external hostname or the backend's expected host, X-Forwarded-Host to the external DNS alias, and X-Forwarded-For with the original client IP. These headers let apps generate correct URLs, handle logins properly, and write accurate access logs.

Yes. All four support Windows. You can run them as native Windows services, inside Docker containers via Docker Desktop, or through Windows Subsystem for Linux (WSL). WSL2 generally gives the smoothest experience for HAProxy and Envoy, while NGINX and Traefik have solid native Windows builds.