Context
Modulo currently forwards the original {@code Host} header to the upstream WO app verbatim (see {@code ModuloProxy.addProxyHeaders}, look for the {@code FIXME} note). This emulates Apache's {@code ProxyPreserveHost On} directive.
The reason: WO apps — and wonder/ERX especially — read the {@code host} request header directly to derive {@code request.serverName()} and to generate absolute URLs. Without preserving the original host, they see modulo's upstream target (e.g. {@code hz1.rebbi.is:2008}) instead of the public hostname the browser asked for (e.g. {@code www.undirskriftasofnun.is}). Under Apache→modulo this happened to work, because Apache forwarded the original host with {@code ProxyPreserveHost On} and modulo's own host-rewriting was a second hop that left the X-Forwarded-* chain intact.
This is not what a modern reverse proxy is supposed to do. Jetty's default — and the RFC-aligned behavior — is to rewrite {@code Host} to the upstream's host:port, and signal the original through {@code X-Forwarded-Host} / {@code X-Forwarded-Proto} / {@code Forwarded} (RFC 7239). Modulo is short-circuiting that for compatibility.
What's wrong with the current shape
- It bakes a behavioral assumption into modulo that's specific to legacy WO apps reading {@code host} directly.
- It collides with anything downstream that expects {@code Host} to match the upstream's identity (TLS upstream connections that validate Host, virtual-host-aware upstreams, anything that cares about the conventional contract).
- It silently masks the real {@code X-Forwarded-Host} mechanism. Apps that do use {@code X-Forwarded-Host} get no value from modulo because we never set one.
What we want
A clean migration path that ends with:
- Modulo emits {@code X-Forwarded-Host}, {@code X-Forwarded-For}, {@code X-Forwarded-Proto} (and/or RFC 7239 {@code Forwarded}) on every proxied request, populated correctly whether modulo is front-facing or behind another proxy.
- Modulo stops rewriting {@code Host} for the upstream — it goes through unchanged from Jetty's proxy default (which sets it to the upstream's host:port).
- WO/wonder is updated so {@code request.serverName()} and friends prefer {@code X-Forwarded-Host} over {@code Host} when present.
Step 3 is the gating item — until WO/wonder honors X-Forwarded-Host, modulo can't drop the workaround without breaking apps.
Migration sketch
- Add {@code X-Forwarded-*} emission in modulo (does no harm even with the host-preserve workaround still in place).
- Patch wonder/ERX (or its slim equivalent) to honor {@code X-Forwarded-Host} in {@code WORequest.serverName()} and absolute-URL generation. Probably small.
- Deploy updated wonder to apps.
- Once apps are on the updated wonder, drop the {@code Host}-preserve override in {@code ModuloProxy} and let Jetty's default behavior take over.
Related
Context
Modulo currently forwards the original {@code Host} header to the upstream WO app verbatim (see {@code ModuloProxy.addProxyHeaders}, look for the {@code FIXME} note). This emulates Apache's {@code ProxyPreserveHost On} directive.
The reason: WO apps — and wonder/ERX especially — read the {@code host} request header directly to derive {@code request.serverName()} and to generate absolute URLs. Without preserving the original host, they see modulo's upstream target (e.g. {@code hz1.rebbi.is:2008}) instead of the public hostname the browser asked for (e.g. {@code www.undirskriftasofnun.is}). Under Apache→modulo this happened to work, because Apache forwarded the original host with {@code ProxyPreserveHost On} and modulo's own host-rewriting was a second hop that left the X-Forwarded-* chain intact.
This is not what a modern reverse proxy is supposed to do. Jetty's default — and the RFC-aligned behavior — is to rewrite {@code Host} to the upstream's host:port, and signal the original through {@code X-Forwarded-Host} / {@code X-Forwarded-Proto} / {@code Forwarded} (RFC 7239). Modulo is short-circuiting that for compatibility.
What's wrong with the current shape
What we want
A clean migration path that ends with:
Step 3 is the gating item — until WO/wonder honors X-Forwarded-Host, modulo can't drop the workaround without breaking apps.
Migration sketch
Related