IIS Web Farm with ARR: Load Balancing, Health Probes, and Session State

A single IIS server eventually hits a ceiling. The two ways past it are scale up (a larger VPS) or scale out (several nodes behind a load balancer). On Windows Server the native scale-out answer is an IIS Web Farm fronted by Application Request Routing (ARR) with URL Rewrite. This walkthrough stands up a two-node farm on separate Windows VPS instances, configures ARR as a reverse proxy, and adds health probes so a failed node drops out without manual intervention. Provisioning the nodes is the first step — the comparison of Windows VPS providers lists plans with matching regions, which matters when nodes must share a low-latency network. When one box is no longer enough, that page is the starting point for choosing the front-end and backend hosts.

1. Architecture and Prerequisites

  • Front end — one small Windows Server with IIS, ARR, and URL Rewrite. Terminates TLS, balances, and runs health checks.
  • Backends — two or more IIS nodes serving identical content.
  • Shared state — sessions and content must be shared or stateless, or users bounce between nodes and lose carts and logins.

Place all nodes in the same region so intra-farm latency stays under 2 ms. Cross-region backends introduce TLS renegotiation and session lag that health probes mask but users feel.

2. Install ARR and URL Rewrite

Install the IIS role, then add Application Request Routing 3.0 and URL Rewrite 2.1 from Microsoft. Verify both modules loaded:

Get-WebGlobalModule | Where-Object Name -like '*ARR*'

The output should list ApplicationRequestRouting and its provider modules. If nothing returns, the install did not register — reinstall with the Web Platform Installer (WebPI) or the MSI directly.

3. Create the Server Farm and Probes

In IIS Manager, open Server Farms > Create Server Farm. Add both backend addresses. On the Health Test tab, set a URL the app exposes cheaply (a static /health.txt is ideal — do not probe a database-backed endpoint), an interval of 10 seconds, and a failure threshold of 3. That means a bad node is removed within 30 seconds.

Probe settingRecommended valueEffect
URL/health.txt (static)Cheap, avoids DB load per probe
Interval10 sDetects failure within 30 s at threshold 3
Timeout5 sRejects slow nodes before they cascade
Failure threshold3Prevents flapping on a single dropped packet

4. Load Balancing Algorithm and Affinity

ARR defaults to weighted round-robin. For stateless ASP.NET Core apps that is correct. For classic ASP.NET sessions backed by InProc, either move to StateServer/SQLServer or enable client affinity — but affinity undermines the point of a farm, so fix session storage instead. Set a sticky cookie only for short-lived migration windows.

5. X-Forwarded Headers

Behind ARR, every request appears to originate from the proxy. Rewrite the client identity headers so logs and rate-limiting see the real IP:

  • Enable X-Forwarded-For and X-Forwarded-Proto in the farm’s proxy settings.
  • In ASP.NET Core, add ForwardedHeaders middleware and clear KnownNetworks/KnownProxies defaults to trust only your front-end.
  • In classic ASP.NET, read the header in Application_BeginRequest and update Request.UserHostAddress carefully — do not trust it from arbitrary clients.

6. Verifying Failover

Test rather than trust. Stop the Application Pool on one backend, then poll the farm URL and watch which node answers. A correctly configured farm returns 200 throughout, with the dead node showing Unhealthy in the ARR farm view within the threshold window. If requests still land on the dead node, the probe URL is wrong or the failure threshold is too high.

7. Handling Sessions on a Web Farm

Round-robin balancing exposes any session state held in-process. Three options, in order of preference:

  • Stateless tokens — for ASP.NET Core, store state in a JWT or a signed cookie and skip server sessions entirely.
  • Distributed cache — Redis or a SQL-backed session store shared by all nodes; adds a dependency but scales cleanly.
  • Client affinity — sticky sessions via the ARR affinity cookie. Simplest, but a node failure logs out its users and uneven traffic follows.

For file uploads, do not write to local disk on one node — the next request may hit another. Use a shared SMB share, Azure Blob, or object storage consistent across nodes.

Common Failure Modes

SymptomLikely causeCheck
502 from the front endBackend unreachable or probe says downFarm health view, backend firewall
Node flappingProbe timeout too tightRaise timeout to 5 s, threshold to 3
Random logoutsInProc session stateMove to distributed cache
Wrong client IP in logsForwarded headers not enabledX-Forwarded-For settings
Stale content on one nodeDeploy missed a nodeDeployment script targets all nodes

Cost vs Complexity

An ARR farm reuses the IIS skills and licensing you already have, which makes it the cheapest scale-out route on Windows. The trade-off is that ARR itself is a single point of failure until you add a second front end or a DNS-level failover. Size the backends for peak-single-node load so a surviving node can carry the traffic during a failure — with the current pricing laid out on the Windows VPS plan comparison, that headroom is a line-item decision, not a guess.

Leave a Comment