Skip to content

Aspire localhost TLD (*.dev.localhost) URLs break Codespaces port forwarding #14236

@mitchdenny

Description

@mitchdenny

Summary

When using Aspire's --localhost-tld option (which creates URLs like https://myapp.dev.localhost:17000), the terminal URL detection in GitHub Codespaces fails to automatically forward ports. Users must manually forward ports through the Codespaces UI.

Reproduction Steps

  1. Create a new Aspire project in a GitHub Codespace with the localhost TLD option enabled:
    aspire new --localhost-tld
  2. Run the project:
    dotnet run --project MyApp.AppHost
  3. Observe the terminal output displays URLs like:
    Now listening on: https://myapp-apphost.dev.localhost:17000
    Login to the dashboard at https://myapp-apphost.dev.localhost:17000/login?t=...
    
  4. Expected: Codespaces should auto-detect the localhost URL and offer to forward the port
  5. Actual: No port forwarding is triggered; the URL is not clickable/accessible

Root Cause

The CodespacesResourceUrlRewriterService only rewrites URLs when the host is exactly "localhost":

// src/Aspire.Hosting/Devcontainers/Codespaces/CodespacesResourceUrlRewriterService.cs:36
if (!originalUrlSnapshot.IsInternal && (uri.Scheme == "http" || uri.Scheme == "https") && uri.Host == "localhost")

When --localhost-tld is enabled, the dashboard URL becomes https://myproject-apphost.dev.localhost:17000. This does not match the uri.Host == "localhost" condition because uri.Host is myproject-apphost.dev.localhost, not localhost.

As a result:

  1. The Codespaces URL rewriter skips these URLs
  2. The terminal outputs *.dev.localhost URLs unchanged
  3. Codespaces' terminal URL detection doesn't recognize these as localhost URLs
  4. Port forwarding is not triggered automatically

Proposed Fix

Use the existing EndpointHostHelpers.IsLocalhostOrLocalhostTld() helper method which already handles all localhost variants:

// Before (line 36):
if (!originalUrlSnapshot.IsInternal && (uri.Scheme == "http" || uri.Scheme == "https") && uri.Host == "localhost")

// After:
if (!originalUrlSnapshot.IsInternal && (uri.Scheme == "http" || uri.Scheme == "https") 
    && EndpointHostHelpers.IsLocalhostOrLocalhostTld(uri))

This is a minimal, single-line change that leverages existing infrastructure.

Affected Files

  • src/Aspire.Hosting/Devcontainers/Codespaces/CodespacesResourceUrlRewriterService.cs (primary)
  • Possibly src/Aspire.Hosting/Devcontainers/Codespaces/CodespacesUrlRewriter.cs if called from other paths

Alternative Considerations

  1. Auto-disable --localhost-tld in Codespaces: Detect the environment and warn or fallback to regular localhost URLs
  2. Documentation: Document that --localhost-tld doesn't work optimally in Codespaces (not ideal UX)
  3. Hybrid display: Show both Codespaces URL and original .dev.localhost URL in terminal

Environment

  • Aspire version: main branch / .NET 10
  • Environment: GitHub Codespaces
  • Feature: --localhost-tld template option

Additional Context

  • The .localhost TLD is an IETF reserved TLD (RFC 2606) that always resolves to 127.0.0.1
  • Browsers and OS handle .localhost specially, but port forwarding tools (including Codespaces) often only look for exact localhost matches
  • The helper method EndpointHostHelpers.IsLocalhostOrLocalhostTld() already exists in the codebase for this exact purpose

Metadata

Metadata

Assignees

Labels

area-app-modelIssues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions