{"record":{"id":"ba5b328ec97afbff","repo":"gofiber/fiber","slug":"errredirectdowngrade","errorCode":"ErrRedirectDowngrade","errorMessage":"proxy: HTTPS to HTTP redirect blocked","messagePattern":"proxy: HTTPS to HTTP redirect blocked","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/proxy/security.go","lineNumber":66,"sourceCode":"var (\n\t// ErrUpstreamSchemeNotAllowed is returned when the proxied URL uses a\n\t// scheme outside the configured allowlist (default: http, https).\n\tErrUpstreamSchemeNotAllowed = errors.New(\"proxy: upstream scheme is not allowed\")\n\n\t// ErrUpstreamHostInvalid is returned when the proxied URL is missing a\n\t// host or cannot be parsed.\n\tErrUpstreamHostInvalid = errors.New(\"proxy: upstream host is empty or invalid\")\n\n\t// ErrUpstreamHostBlocked is returned when the proxied URL resolves to\n\t// an address inside a blocked range (loopback, RFC 1918 private,\n\t// link-local, multicast, unspecified, or CGNAT) and AllowPrivateIPs\n\t// is false.\n\tErrUpstreamHostBlocked = errors.New(\"proxy: upstream host resolves to a blocked address\")\n\n\t// ErrRedirectDowngrade is returned when DoRedirects encounters a\n\t// redirect from an HTTPS upstream to a plaintext HTTP target and\n\t// AllowHTTPSDowngrade is false.\n\tErrRedirectDowngrade = errors.New(\"proxy: HTTPS to HTTP redirect blocked\")\n)\n\n// SecurityPolicy controls runtime security restrictions applied to the\n// proxy.Do, proxy.Forward, proxy.DoRedirects, proxy.DoTimeout, and\n// proxy.DoDeadline runtime helpers as well as Balancer instances that\n// do not supply their own policy via Config.SecurityPolicy.\ntype SecurityPolicy struct {\n\t// AllowedSchemes restricts the URL schemes accepted as upstream\n\t// targets. Empty defaults to []string{schemeHTTP, schemeHTTPS}.\n\tAllowedSchemes []string\n\n\t// AllowPrivateIPs allows upstream hosts to resolve to loopback,\n\t// private (RFC 1918), link-local, multicast, unspecified, or CGNAT\n\t// (RFC 6598) addresses. SECURITY: enabling this exposes the proxy\n\t// to SSRF attacks against internal services such as cloud\n\t// metadata endpoints. Default: false.\n\t//\n\t// DNS-rebinding scope: when false, the resolved IP is re-validated at","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/proxy/security.go#L48-L84","documentation":"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.","triggerScenarios":"An HTTPS upstream returns 301/302/303/307/308 with a Location header whose URL is http://, while SecurityPolicy.AllowHTTPSDowngrade is false (default).","commonSituations":"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.","solutions":["Fix the upstream to redirect to an https:// Location.","If the downgrade is acceptable in your environment, set SecurityPolicy.AllowHTTPSDowngrade=true and record the security trade-off.","Disable redirect following (use proxy.Do instead of proxy.DoRedirects) and handle redirects explicitly.","Pin allowed redirect targets so an open redirect can't force a downgrade."],"exampleFix":"// before\ncfg := proxy.Config{ /* AllowHTTPSDowngrade defaults to false */ }\nreturn proxy.DoRedirects(c, url, cfg)\n\n// after (only if downgrade is acceptable)\ncfg := proxy.Config{\n    SecurityPolicy: &proxy.SecurityPolicy{AllowHTTPSDowngrade: true},\n}\nreturn proxy.DoRedirects(c, url, cfg)","handlingStrategy":"validation","validationCode":"if u.Scheme == \"https\" && policy.AllowHTTPSDowngrade == false {\n    // after each redirect, re-check the new Location's scheme\n    if loc, err := res.Location(); err == nil && loc.Scheme == \"http\" {\n        return fiber.NewError(fiber.StatusBadGateway, \"refusing TLS downgrade\")\n    }\n}","typeGuard":"func isDowngrade(from, to *url.URL) bool { return from.Scheme == \"https\" && to.Scheme == \"http\" }","tryCatchPattern":null,"preventionTips":["Pin allowed redirect hosts so an open redirect can't force a downgrade.","Prefer DoRedirects with a SecurityPolicy over manual redirect loops.","Default-deny downgrades unless your environment explicitly tolerates them."],"tags":["proxy","security","tls","redirect","https"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}