Skip to content

Stop emulating ProxyPreserveHost — migrate WO apps to X-Forwarded-Host #7

@hugithordarson

Description

@hugithordarson

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:

  1. 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.
  2. 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).
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions