Skip to content

Unexpected Proxy Usage for *.localhost with S3AsyncClient + Netty #6082

@lucashbarbosa

Description

@lucashbarbosa

Describe the bug

When using S3AsyncClient (Netty-based) to connect to LocalStack or other local services, the SDK builds virtual-hosted–style URLs such as:
http://my-bucket.localhost:4566.

Even with proxy exclusions like NO_PROXY=localhost,127.0.0.1 or .nonProxyHosts(Set.of("localhost", "127.0.0.1")), the SDK still routes the request through the configured proxy. This is unexpected and leads to failures when behind a corporate proxy.

This behavior breaks most LocalStack-based development workflows and contradicts standard expectations for proxy exclusion handling.

Expected Behavior

When configuring proxy exclusions using NO_PROXY=localhost,127.0.0.1 or setting .nonProxyHosts(Set.of("localhost", "127.0.0.1")), the SDK should not route any requests to localhost through a proxy, regardless of whether the hostname is exactly localhost or a subdomain like my-bucket.localhost.

Since localhost and its subdomains resolve to the loopback interface and are intended for local communication, proxying such requests defeats the purpose and typically results in connection failures, especially in development environments that rely on local containers or mock services like LocalStack.

Therefore, if the final resolved host is:

localhost

127.0.0.1

or ends with .localhost

…it should be excluded from proxy usage entirely.

This matches standard behavior across tools, libraries, and languages where NO_PROXY=localhost implicitly applies to any *.localhost subdomain.

Current Behavior

When using S3AsyncClient with the default virtual-hosted–style addressing, the SDK generates URLs like http://my-bucket.localhost:4566, where the bucket name becomes part of the host.

Internally, the SDK evaluates whether to use a proxy by checking if the full host — in this case, my-bucket.localhost — matches any entry in NO_PROXY or nonProxyHosts.

However, since my-bucket.localhost does not match localhost or 127.0.0.1 exactly, the SDK assumes it should use the configured proxy. As a result, requests to LocalStack (or other local services) are incorrectly routed through the proxy, leading to errors such as proxy tunnel failures or timeouts.

This happens even though the user has clearly configured NO_PROXY=localhost, and would reasonably expect this to apply to any address that resolves to the loopback interface, including subdomains like my-bucket.localhost.

Reproduction Steps

1 - Start LocalStack on http://localhost:4566 in a proxy environment

2 - Configure environment variables:

HTTP_PROXY=http://proxy.mycompany.com:3128
HTTPS_PROXY=http://proxy.mycompany.com:3128
NO_PROXY=localhost,127.0.0.1

3 - Create a client using:

S3AsyncClient.builder().endpointOverride(URI.create("http://localhost:4566")).region(Region.US_EAST_1).build()

4 - Call any operation, for example:

client.getObject(GetObjectRequest.builder().bucket("my-bucket").key("test.txt").build(), AsyncResponseTransformer.toBytes())

5 - Even though localhost is excluded, the SDK routes the request to the proxy. This results in errors such as:

Unable to establish tunnel for channel: my-bucket.localhost
or
504 Gateway Timeout if the proxy cannot resolve or reach my-bucket.localhost

Possible Solution

Add .*.localhost to nonProxyHosts

Disable proxy usage explicitly when targeting localhost

Force path-style addressing (if feasible)

Treat any host ending with .localhost as a proxy exclusion

If localhost is listed in NO_PROXY or nonProxyHosts, implicitly exclude all *.localhost as well

Allow an easy toggle to enforce path-style addressing

Improve documentation regarding proxy behavior with virtual-hosted addressing and localhost

Additional Information/Context

No response

AWS Java SDK version used

2.23.14

JDK version used

17.0.5

Operating System and version

CentOS Red Hat Enterprise Linux release 8.10 (Ootpa) Kernel: 4.18.0-553.50.1.el8_10.x86_64

Metadata

Metadata

Assignees

Labels

bugThis issue is a bug.p2This is a standard priority issueresponse-requestedWaiting on additional info and feedback. Will move to "closing-soon" in 10 days.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions