# Java behind IIS or Nginx: proxy headers, WebSocket and memory

> Put Tomcat behind IIS ARR or Nginx, forward the real host/proto/client IP, keep WebSocket alive, and size the JVM instead of guessing.

**Tested on**

- Build: 26100.33650
- Edition: Standard / Datacenter
- Architecture: x64
- Evidence: E1
- Verified: 2026-10-29

## The short answer

Terminate TLS at IIS/Nginx, proxy to Tomcat on `127.0.0.1:8080`:

- forward `Host`, `X-Forwarded-For`, `X-Forwarded-Proto`;
- configure Tomcat’s `RemoteIpValve` so the app sees the client, not the proxy;
- allow WebSocket upgrades and long read timeouts;
- size the JVM (`-Xms`/`-Xmx`) and Tomcat threads separately.

## Nginx

```nginx
location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    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_read_timeout 60s;
}
```

WebSocket:

```nginx
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
```

`map $http_upgrade $connection_upgrade` goes in the `http` block.

## IIS ARR

- Create a server farm pointing at `127.0.0.1:8080`.
- Preserve `Host`; forward `X-Forwarded-For`/`X-Forwarded-Proto`.
- Increase proxy timeout for long requests; enable WebSocket if needed.
- Do not expose Tomcat’s 8080 directly to the internet.

## Tomcat sees the proxy

Enable `RemoteIpValve` in `server.xml` (inside the `Host`):

```xml
<Valve className="org.apache.catalina.valves.RemoteIpValve"
       remoteIpHeader="x-forwarded-for"
       protocolHeader="x-forwarded-proto"
       protocolHeaderHttpsValue="https" />
```

Without it, `request.getRemoteAddr()` returns `127.0.0.1`, and `request.isSecure()` may be false unless the connector is configured for `scheme="https"`.

## Memory and threads

```bat
set "JAVA_OPTS=-Xms1g -Xmx1g -XX:+UseG1GC"
```

- Set `-Xms` = `-Xmx` for a server with dedicated memory; avoids heap growth pauses.
- Leave headroom for Metaspace, thread stacks, direct buffers and the OS.
- Tomcat `maxThreads` is request concurrency, not heap. Raising threads does not fix a memory leak.
- Check GC logs and `jcmd <pid> GC.heap_info` before changing numbers.

## Verify

```powershell
curl.exe -I https://lab.example/
Get-Content C:\tomcat\logs\localhost.*.log -Tail 50
jcmd <pid> VM.system_properties | Select-String "java.home|java.version"
```

Check the app logs the real client IP and `https` scheme; check the JVM is the pinned JDK.

> **Caution: Do not trust forwarded headers from the internet**
>
> `X-Forwarded-For` is spoofable if the client can reach Tomcat directly. Bind Tomcat to localhost/private IP and only trust the proxy’s header.

## Lab boundary

E1. The lab did not run Tomcat or a Java app; the configuration is the proxy pattern to verify on Server 2025.

## Sources

- [Apache Tomcat — Valves](https://tomcat.apache.org/tomcat-10.1-doc/config/valve.html)
- [Apache Tomcat — HTTP connector](https://tomcat.apache.org/tomcat-10.1-doc/config/http.html)
- [nginx — WebSocket proxying](https://nginx.org/en/docs/http/websocket.html)

---

Source: https://windows.homes/en/server/runtimes/java-proxy/
