You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Now that modulo is the front-facing server (not behind Apache), it has inherited the job of injecting the request headers that WO/wonder apps expect an adaptor to set. We've hit this class of bug twice already in production — each time as a separate surprise:
remote_addr / remote_host — apps read these for client IP; modulo only sourced them from x-forwarded-for, which is null when front-facing, so the headers were absent and ERXHTTPUtilities.ipAddressFromRequest() returned null. Fixed in commit 0d5afb1.
Rather than keep discovering these one production incident at a time, this issue captures a full audit of what the apps actually read, so the header situation can be tackled as a whole.
The audit was done against the two header-consuming codebases:
undur-webobjects/JavaWebObjects
wonder-slim/ERExtensions
What the apps read
Client IP — ERXRequest.HOST_ADDRESS_HEADERS, ERXHTTPUtilities, WOStatisticsStore
Checked in order: x-forwarded-for, pc-remote-addr, remote_host, remote_addr, remote_user, x-webobjects-remote-addr. Status: handled — modulo sets remote_addr + remote_host from the real peer (or x-forwarded-for when behind a proxy) as of 0d5afb1.
Checked in order: x-forwarded-host, Host, x-webobjects-server-name, server_name, http_host. Status: partially handled — modulo overwrites Host with the original authority, which works because ERXRequest falls through to Host. But the intended mechanism is x-forwarded-host, checked first. We're relying on the fallback. Setting x-forwarded-host would be more correct and is a prerequisite for dropping the Host override later (see #7).
HTTPS detection — ERXRequest.isRequestSecure()
Checks, in order: SERVER_PORT == 443, x-webobjects-servlet-server-port, x-webobjects-server-port, https == "on", x-forwarded-proto == "https". Status: NOT handled — real latent bug. modulo sets none of these. When a browser hits modulo over HTTPS (TLS terminated at modulo, plain HTTP forwarded to the app), the app sees no signal that the public request was secure, so isRequestSecure() returns false. Any app code generating https:// absolute URLs, setting Secure cookies, or doing scheme-dependent logic is affected. The fix is to set x-forwarded-proto: https when the inbound connection was secure (ERXRequest already reads it).
WOStatisticsStore reads x-webobjects-remote-host / x-webobjects-remote-addr / remote_user as logging fallbacks — satisfied once remote_addr/remote_host are present.
Proposed work (tackle as a set)
Set x-forwarded-proto: https when the inbound connection is secure. Fixes isRequestSecure(). Highest priority — it's a real currently-broken capability.
Set x-forwarded-host to the original authority. More correct than relying on the Host fallback; unblocks dropping the Host override.
Decide the canonical mechanism for each concern (forwarded-* standard headers vs. the WO-specific x-webobjects-* vs. the legacy remote_*) and set them consistently, rather than the current incremental "set the one that broke today" approach.
Consider the SSL/port headers (SERVER_PORT / x-webobjects-server-port) — x-forwarded-proto covers the modern path, so these may be unnecessary, but worth a deliberate decision rather than leaving a gap.
#7 is about migrating apps off the Host override onto standard forwarded headers. Items 1 and 2 here are the first half of that migration — setting the standard headers — and #7's "stop overwriting Host" is the second half. The two issues should be resolved together; this one is the broader "get all the adaptor headers right" framing, #7 is the specific Host-override cleanup.
Note on philosophy
modulo is the WO-aware proxy — getting these headers right is precisely the value it adds over a generic reverse proxy. This is core, not polish.
Context
Now that modulo is the front-facing server (not behind Apache), it has inherited the job of injecting the request headers that WO/wonder apps expect an adaptor to set. We've hit this class of bug twice already in production — each time as a separate surprise:
host(andx-forwarded-host) to build URLs / deriveserverName(). Fixed by preserving the original authority (Stop emulating ProxyPreserveHost — migrate WO apps to X-Forwarded-Host #7).x-forwarded-for, which is null when front-facing, so the headers were absent andERXHTTPUtilities.ipAddressFromRequest()returned null. Fixed in commit0d5afb1.Rather than keep discovering these one production incident at a time, this issue captures a full audit of what the apps actually read, so the header situation can be tackled as a whole.
The audit was done against the two header-consuming codebases:
undur-webobjects/JavaWebObjectswonder-slim/ERExtensionsWhat the apps read
Client IP —
ERXRequest.HOST_ADDRESS_HEADERS,ERXHTTPUtilities,WOStatisticsStoreChecked in order:
x-forwarded-for,pc-remote-addr,remote_host,remote_addr,remote_user,x-webobjects-remote-addr.Status: handled — modulo sets
remote_addr+remote_hostfrom the real peer (orx-forwarded-forwhen behind a proxy) as of0d5afb1.Hostname / URL generation —
ERXRequest.HOST_NAME_HEADERSChecked in order:
x-forwarded-host,Host,x-webobjects-server-name,server_name,http_host.Status: partially handled — modulo overwrites
Hostwith the original authority, which works because ERXRequest falls through toHost. But the intended mechanism isx-forwarded-host, checked first. We're relying on the fallback. Settingx-forwarded-hostwould be more correct and is a prerequisite for dropping theHostoverride later (see #7).HTTPS detection —
ERXRequest.isRequestSecure()Checks, in order:
SERVER_PORT== 443,x-webobjects-servlet-server-port,x-webobjects-server-port,https== "on",x-forwarded-proto== "https".Status: NOT handled — real latent bug. modulo sets none of these. When a browser hits modulo over HTTPS (TLS terminated at modulo, plain HTTP forwarded to the app), the app sees no signal that the public request was secure, so
isRequestSecure()returns false. Any app code generatinghttps://absolute URLs, settingSecurecookies, or doing scheme-dependent logic is affected. The fix is to setx-forwarded-proto: httpswhen the inbound connection was secure (ERXRequest already reads it).Already handled / pass-through
cookie— passes through; modulo also coalesces multiple H/2 cookie headers (008f5bd).x-webobjects-request-id— modulo sets it.content-type,content-length,accept-encoding,accept-language,user-agent— pass through untouched.Lower priority / situational
x-webobjects-recording— recording/playback feature; niche.pc-remote-addr— legacy adaptor variant nobody sets; ignore.WOStatisticsStorereadsx-webobjects-remote-host/x-webobjects-remote-addr/remote_useras logging fallbacks — satisfied onceremote_addr/remote_hostare present.Proposed work (tackle as a set)
x-forwarded-proto: httpswhen the inbound connection is secure. FixesisRequestSecure(). Highest priority — it's a real currently-broken capability.x-forwarded-hostto the original authority. More correct than relying on theHostfallback; unblocks dropping theHostoverride.x-webobjects-*vs. the legacyremote_*) and set them consistently, rather than the current incremental "set the one that broke today" approach.SERVER_PORT/x-webobjects-server-port) —x-forwarded-protocovers the modern path, so these may be unnecessary, but worth a deliberate decision rather than leaving a gap.Relationship to #7
#7 is about migrating apps off the
Hostoverride onto standard forwarded headers. Items 1 and 2 here are the first half of that migration — setting the standard headers — and #7's "stop overwriting Host" is the second half. The two issues should be resolved together; this one is the broader "get all the adaptor headers right" framing, #7 is the specific Host-override cleanup.Note on philosophy
modulo is the WO-aware proxy — getting these headers right is precisely the value it adds over a generic reverse proxy. This is core, not polish.