Fix HTTPS scheme detection behind reverse proxy

When behind a reverse proxy that terminates SSL, the server sees HTTP
requests internally. Added _get_base_url() helper that respects the
X-Forwarded-Proto header to generate correct external HTTPS URLs.

This fixes links in the PyPI simple index showing http:// instead of
https:// when accessed via HTTPS through a load balancer.
This commit is contained in:
Mondo Diaz
2026-01-29 18:02:21 -06:00
parent f58fb0079a
commit 796176c251

View File

@@ -81,6 +81,26 @@ def _get_basic_auth(source) -> Optional[tuple[str, str]]:
return None
def _get_base_url(request: Request) -> str:
"""
Get the external base URL, respecting X-Forwarded-Proto header.
When behind a reverse proxy that terminates SSL, the request.base_url
will show http:// even though the external URL is https://. This function
checks the X-Forwarded-Proto header to determine the correct scheme.
"""
base_url = str(request.base_url).rstrip('/')
# Check for X-Forwarded-Proto header (set by reverse proxies)
forwarded_proto = request.headers.get('x-forwarded-proto')
if forwarded_proto:
# Replace the scheme with the forwarded protocol
parsed = urlparse(base_url)
base_url = f"{forwarded_proto}://{parsed.netloc}{parsed.path}"
return base_url
def _rewrite_package_links(html: str, base_url: str, package_name: str, upstream_base_url: str) -> str:
"""
Rewrite download links in a PyPI simple page to go through our proxy.
@@ -189,7 +209,7 @@ async def pypi_simple_index(
content = response.text
# Rewrite package links to go through our proxy
base_url = str(request.base_url).rstrip('/')
base_url = _get_base_url(request)
content = re.sub(
r'href="([^"]+)/"',
lambda m: f'href="{base_url}/pypi/simple/{m.group(1)}/"',
@@ -235,7 +255,7 @@ async def pypi_package_versions(
detail="No PyPI upstream sources configured"
)
base_url = str(request.base_url).rstrip('/')
base_url = _get_base_url(request)
# Normalize package name (PEP 503)
normalized_name = re.sub(r'[-_.]+', '-', package_name).lower()