Your browser can report h2 while the application behind the load balancer still receives HTTP/1.1. Those are different connections. If you are evaluating a request-smuggling mitigation, the upstream connection belongs in the picture too.
Where the downgrade happens
browser -- HTTP/2 --> reverse proxy -- HTTP/1.1 --> applicationThe proxy terminates the client connection and translates the request for the next hop. PortSwigger's HTTP/2 downgrading explanation describes this conversion and the request-smuggling risks it can introduce.
That translation puts HTTP/1 request-boundary parsing back into the path. If the proxy and the receiving server disagree about where a request ends, the browser's HTTP/2 connection has not removed that boundary. Downgrading is a boundary to examine, not proof of a vulnerability.
A concrete example: AWS Application Load Balancer
AWS's target-group documentation says ALBs send requests to targets using HTTP/1.1 by default. HTTP/2 is a separate target-group protocol-version setting.
The HTTP rows of its compatibility matrix are worth reading together:
| Incoming request | Target-group protocol version | Documented result |
|---|---|---|
| HTTP/1.1 | HTTP/1.1 | Success |
| HTTP/2 | HTTP/1.1 | Success |
| HTTP/1.1 | HTTP/2 | Error |
| HTTP/2 | HTTP/2 | Success |
“Success” here is a protocol-compatibility outcome, not a security rating. The awkward row is HTTP/1.1 → HTTP/2: changing the target group is not a transparent upgrade for every client. AWS also requires an HTTPS listener, a forward action and instance or ip targets for HTTP/2 target groups.
Read the configured target hop
For an ALB you operate, the AWS CLI's describe-target-groups command exposes the relevant fields. Use a configured CLI identity in the correct account/region with permission to describe target groups, and replace the placeholder with your ALB ARN:
aws elbv2 describe-target-groups \
--load-balancer-arn '<your-alb-arn>' \
--query 'TargetGroups[].{Name:TargetGroupName,Protocol:Protocol,Version:ProtocolVersion}' \
--output tableRead Version (HTTP1, HTTP2 or GRPC) separately from Protocol (HTTP/HTTPS for these target groups). Encryption to the target does not establish its HTTP version.
This read-only command lists associated groups and their configuration. Match the request's listener forwarding rule to the relevant target group before interpreting the result; the list does not identify which group handled an individual request. A further proxy behind that target is another hop to inspect.
I’d map that actual route before calling “HTTP/2 enabled” a desync mitigation. The browser indicator answers a narrower question.
If you've removed an HTTP/1.1 upstream hop from a service with mixed HTTP/1.1 and HTTP/2 clients, which compatibility constraint shaped the design, and how did you confirm the protocol on the actual request path?
