Skip to content

feat(ssh): use tls.domain as Host override even when TLS is disabled#6321

Merged
gustavosbarreto merged 1 commit into
masterfrom
feat/web-endpoint-host-override
May 13, 2026
Merged

feat(ssh): use tls.domain as Host override even when TLS is disabled#6321
gustavosbarreto merged 1 commit into
masterfrom
feat/web-endpoint-host-override

Conversation

@gustavosbarreto
Copy link
Copy Markdown
Member

Closes #6319.

What

Decouples the Host rewrite from endpoint.TLS.Enabled in ssh/http/handlers.go::HandleHTTPProxy. When endpoint.TLS.Domain is non-empty, the outgoing Host header is set to that value regardless of whether TLS-to-backend is enabled. When TLS is enabled, the same value is used as SNI, as before.

Why

Today the proxy only overrides Host inside the TLS branch. Backends served on plain HTTP that validate Host or auto-redirect to a canonical hostname (very common with internal admin panels and corporate apps) cannot be reached through a Web Endpoint, because they receive Host: <address>.<endpoints_domain> and respond with a 302 to their internal canonical hostname, which is not publicly resolvable. The tls.domain field already captures the value such backends expect; treating it as a Host hint independent of TLS makes that case work.

Behavior matrix

tls.enabled tls.domain Host sent to backend Wire to backend
true non-empty tls.domain (unchanged) TLS with SNI=tls.domain (unchanged)
false non-empty tls.domain (new) plain HTTP
false empty <address>.<endpoints_domain> (unchanged) plain HTTP
true empty rejected by service-layer validation (unchanged) n/a

Verification

Validated end-to-end against a local stack with a Python backend on 127.0.0.1:9080 (plain HTTP) that returns 200 only when Host: app.local, otherwise 302 Location: https://app.local/.... Three endpoints created via POST /api/web-endpoints:

Scenario tls.enabled tls.domain Result before this PR Result after this PR
C false app.local 302 Location: https://app.local (host override never ran) 200 OK, backend logs Host: app.local
B (HTTPS 9443, regression check) true app.local 200 OK (already fixed by #6320) 200 OK (preserved)
D false empty 302 Location: https://app.local (default Host caused canonical redirect) unchanged: 302 Location: https://app.local

Notes

  • The UI currently hides the TLS Domain field unless the Enable TLS toggle is on, and the create payload only includes tls.domain when tls.enabled=true. A UI/UX follow-up issue is filed separately to allow setting tls.domain independent of TLS, including renaming the field to reflect its expanded role.

@gustavosbarreto gustavosbarreto requested a review from a team as a code owner May 13, 2026 13:37
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 13, 2026

Claude finished @gustavosbarreto's task in 2m 58s —— View job


Code Review Complete

Reviewed 1 file across code quality, security, testing, Go patterns, and architecture — no issues found. The code looks good as-is.

The change is a clean, well-scoped refactor: moving the req.Host = endpoint.TLS.Domain assignment from inside the if endpoint.TLS.Enabled block to an earlier guard on endpoint.TLS.Domain != "". Since req.Host is only consumed at req.Write(transportConn) after both blocks, the reordering is safe and the behavior matrix in the PR description is correct.


When endpoint.TLS.Domain is non-empty, rewrite the outgoing Host header
to that value regardless of endpoint.TLS.Enabled. This lets users reach
backends that validate Host or redirect to a canonical hostname without
forcing them to also enable TLS-to-backend, common with plain-HTTP apps
that hardcode their canonical FQDN.

Previously the Host override only ran inside the TLS branch, so an HTTP
target with a configured domain would still receive
Host: <address>.<endpoints_domain> and the backend would 302 to its
canonical hostname, which is typically not publicly resolvable.

Behavior matrix after this change:

- tls.enabled=true,  domain set:   Host=domain, SNI=domain (unchanged)
- tls.enabled=false, domain set:   Host=domain, plain HTTP (new)
- tls.enabled=false, domain empty: Host=default, plain HTTP (unchanged)
- tls.enabled=true,  domain empty: rejected by service validation

Closes #6319
@gustavosbarreto gustavosbarreto force-pushed the feat/web-endpoint-host-override branch from a17e632 to 5c31c6e Compare May 13, 2026 13:40
@gustavosbarreto gustavosbarreto enabled auto-merge (rebase) May 13, 2026 13:42
@gustavosbarreto gustavosbarreto merged commit 1e5cce0 into master May 13, 2026
11 checks passed
@gustavosbarreto gustavosbarreto deleted the feat/web-endpoint-host-override branch May 13, 2026 13:48
gustavosbarreto added a commit that referenced this pull request May 13, 2026
The Domain input was hidden unless the TLS toggle was on, and the
create payload only included the tls object when tls.enabled was
true. After #6321 the backend uses tls.domain
as a Host override even when TLS-to-backend is disabled, which is
what users need to reach apps that validate Host or auto-redirect
to a canonical hostname over plain HTTP.

Changes:

- Domain field is always visible. Required only when TLS is on.
- FQDN validation runs whenever Domain is filled, regardless of
  TLS state.
- Payload always includes the tls object when either TLS is on
  or Domain is non-empty, so the backend receives the host hint.
- Labels and hints clarify the dual role:
  * "Use TLS to backend" toggle controls only the proxy-to-backend
    leg; the public URL is always HTTPS.
  * Domain is described as the Host override, becoming SNI when
    TLS is enabled.

Refs #6322
gustavosbarreto added a commit that referenced this pull request May 13, 2026
The Domain input was hidden unless the TLS toggle was on, and the
create payload only included the tls object when tls.enabled was
true. After #6321 the backend uses tls.domain
as a Host override even when TLS-to-backend is disabled, which is
what users need to reach apps that validate Host or auto-redirect
to a canonical hostname over plain HTTP.

Changes:

- Domain field is always visible. Required only when TLS is on.
- FQDN validation runs whenever Domain is filled, regardless of
  TLS state.
- Payload always includes the tls object when either TLS is on
  or Domain is non-empty, so the backend receives the host hint.
- Labels and hints clarify the dual role:
  * "Use TLS to backend" toggle controls only the proxy-to-backend
    leg; the public URL is always HTTPS.
  * Domain is described as the Host override, becoming SNI when
    TLS is enabled.

Refs #6322
gustavosbarreto added a commit that referenced this pull request May 13, 2026
The Domain input was hidden unless the TLS toggle was on, and the
create payload only included the tls object when tls.enabled was
true. After #6321 the backend uses tls.domain
as a Host override even when TLS-to-backend is disabled, which is
what users need to reach apps that validate Host or auto-redirect
to a canonical hostname over plain HTTP.

Changes:

- Domain field is always visible. Required only when TLS is on.
- FQDN validation runs whenever Domain is filled, regardless of
  TLS state.
- Payload always includes the tls object when either TLS is on
  or Domain is non-empty, so the backend receives the host hint.
- Labels and hints clarify the dual role:
  * "Use TLS to backend" toggle controls only the proxy-to-backend
    leg; the public URL is always HTTPS.
  * Domain is described as the Host override, becoming SNI when
    TLS is enabled.

Refs #6322
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Web endpoint: allow Host override independent of TLS to support backends that validate canonical hostname

1 participant