gofiber/fiber · error

ErrRedirectDowngrade

ErrRedirectDowngrade

Error message

proxy: HTTPS to HTTP redirect blocked

What it means

When following upstream redirects (DoRedirects), the proxy refuses to follow an HTTPS-to-HTTP redirect unless AllowHTTPSDowngrade is true. This prevents TLS stripping: a compromised or misconfigured upstream could otherwise redirect the proxy to plaintext, exposing tokens/bodies. The check compares the new Location's scheme against httpsSchemeBytes.

Source

Thrown at middleware/proxy/security.go:66

var (
	// ErrUpstreamSchemeNotAllowed is returned when the proxied URL uses a
	// scheme outside the configured allowlist (default: http, https).
	ErrUpstreamSchemeNotAllowed = errors.New("proxy: upstream scheme is not allowed")

	// ErrUpstreamHostInvalid is returned when the proxied URL is missing a
	// host or cannot be parsed.
	ErrUpstreamHostInvalid = errors.New("proxy: upstream host is empty or invalid")

	// ErrUpstreamHostBlocked is returned when the proxied URL resolves to
	// an address inside a blocked range (loopback, RFC 1918 private,
	// link-local, multicast, unspecified, or CGNAT) and AllowPrivateIPs
	// is false.
	ErrUpstreamHostBlocked = errors.New("proxy: upstream host resolves to a blocked address")

	// ErrRedirectDowngrade is returned when DoRedirects encounters a
	// redirect from an HTTPS upstream to a plaintext HTTP target and
	// AllowHTTPSDowngrade is false.
	ErrRedirectDowngrade = errors.New("proxy: HTTPS to HTTP redirect blocked")
)

// SecurityPolicy controls runtime security restrictions applied to the
// proxy.Do, proxy.Forward, proxy.DoRedirects, proxy.DoTimeout, and
// proxy.DoDeadline runtime helpers as well as Balancer instances that
// do not supply their own policy via Config.SecurityPolicy.
type SecurityPolicy struct {
	// AllowedSchemes restricts the URL schemes accepted as upstream
	// targets. Empty defaults to []string{schemeHTTP, schemeHTTPS}.
	AllowedSchemes []string

	// AllowPrivateIPs allows upstream hosts to resolve to loopback,
	// private (RFC 1918), link-local, multicast, unspecified, or CGNAT
	// (RFC 6598) addresses. SECURITY: enabling this exposes the proxy
	// to SSRF attacks against internal services such as cloud
	// metadata endpoints. Default: false.
	//
	// DNS-rebinding scope: when false, the resolved IP is re-validated at

View on GitHub (pinned to a105acad6c)

Solutions

  1. Fix the upstream to redirect to an https:// Location.
  2. If the downgrade is acceptable in your environment, set SecurityPolicy.AllowHTTPSDowngrade=true and record the security trade-off.
  3. Disable redirect following (use proxy.Do instead of proxy.DoRedirects) and handle redirects explicitly.
  4. Pin allowed redirect targets so an open redirect can't force a downgrade.

Example fix

// before
cfg := proxy.Config{ /* AllowHTTPSDowngrade defaults to false */ }
return proxy.DoRedirects(c, url, cfg)

// after (only if downgrade is acceptable)
cfg := proxy.Config{
    SecurityPolicy: &proxy.SecurityPolicy{AllowHTTPSDowngrade: true},
}
return proxy.DoRedirects(c, url, cfg)
Defensive patterns

Strategy: validation

Validate before calling

if u.Scheme == "https" && policy.AllowHTTPSDowngrade == false {
    // after each redirect, re-check the new Location's scheme
    if loc, err := res.Location(); err == nil && loc.Scheme == "http" {
        return fiber.NewError(fiber.StatusBadGateway, "refusing TLS downgrade")
    }
}

Type guard

func isDowngrade(from, to *url.URL) bool { return from.Scheme == "https" && to.Scheme == "http" }

Prevention

When it happens

Trigger: An HTTPS upstream returns 301/302/303/307/308 with a Location header whose URL is http://, while SecurityPolicy.AllowHTTPSDowngrade is false (default).

Common situations: Misconfigured upstream that redirects to its plaintext port; captive portals; mixed-environment deploys where staging is http but prod is https; load balancers stripping TLS in front of the upstream.

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/ba5b328ec97afbff. Report an issue: GitHub.