Skip to content

[BUG] S3 virtual-hosted-style URL misinterpreted as path-style over HTTP/2 (Host header vs :authority) #1866

Description

@wataribe

Service

S3

AWS API Action

Any S3 API called via virtual-hosted-style URL (e.g. GetObject, PutObject, presigned URL
access) over HTTP/2.

Expected behavior

A virtual-hosted-style request such as https://{bucket}.s3.{region}.localhost.floci.io:4566/{key}
should be routed to the correct bucket/key regardless of whether the client negotiates HTTP/1.1
or HTTP/2, since both are valid ways for a browser to reach an HTTPS endpoint.

Actual behavior

When the client uses HTTP/2 (which browsers select automatically via ALPN over HTTPS),
virtual-hosted-style requests are misinterpreted as path-style requests: the bucket name segment
in the Host is silently ignored, and the first path segment is treated as the bucket name
instead. The same request over HTTP/1.1 (same TLS, same URL) works correctly.

This was initially mistaken for an HTTP vs HTTPS difference ("works over HTTP, fails over
HTTPS"), but forcing HTTP/1.1 over HTTPS with curl --http1.1 proved it succeeds — the actual
variable is the HTTP protocol version, not TLS.

Root cause (confirmed by reading the source): S3VirtualHostFilter determines whether to
rewrite a virtual-hosted-style request by calling requestContext.getHeaderString("Host"). If
this returns null, the filter does nothing and the request falls through to path-style
handling. HTTP/2 (RFC 9113) has no Host header — it uses the :authority pseudo-header
instead. Since S3VirtualHostFilter never reads :authority, it never fires for HTTP/2
requests, and the request is silently handled as path-style with the wrong bucket resolved from
the URL path.

Practical impact: any browser accessing a presigned URL or making a direct virtual-hosted-style
S3 request over HTTPS will use HTTP/2 by default and hit this bug. Only clients that explicitly
downgrade to HTTP/1.1 (e.g. most AWS SDKs, or curl --http1.1) are unaffected — this is why
server-to-server calls (e.g. via AWS SDK for Java) worked fine while browser-based access failed
consistently.

This is unrelated to #1625 (which only affects bucket-less s3. hosts) — this bug affects
virtual-hosted-style hosts that do include a bucket name, and has been present since
virtual-hosted-style support was added (reproduced on v1.5.15 through v1.5.31).

Reproduction

# Same URL, same TLS/cert, only the negotiated HTTP version differs.
# Bucket "my-bucket" exists and contains key "nested/object.png".

# HTTP/1.1 (forced): correctly resolves bucket "my-bucket", key "nested/object.png" -> 200
curl -i --http1.1 --resolve my-bucket.s3.localhost.floci.io:4566:127.0.0.1 \
  "https://my-bucket.s3.localhost.floci.io:4566/nested/object.png"

# HTTP/2 (curl default over HTTPS via ALPN): bucket name from Host is ignored;
# "nested" is misinterpreted as the bucket name and "object.png" as the key -> 404 NoSuchBucket
curl -i --resolve my-bucket.s3.localhost.floci.io:4566:127.0.0.1 \
  "https://my-bucket.s3.localhost.floci.io:4566/nested/object.png"

Environment

  • Floci version / image tag: v1.5.31 (floci/floci:latest as of 2026-07-07); also reproduced on
    v1.5.15, v1.5.27, v1.5.28
  • Java SDK version (if applicable): N/A for the failing case (browser fetch/<img> requests
    via presigned URL). AWS SDK for Java is unaffected because it uses HTTP/1.1.
  • How you're running Floci (Docker / native / mvn quarkus:dev): Docker (docker compose)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingreleaseds3Amazon Simple Storage Service (S3)

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions