Why does the HTTP Upgrade: header contain both h2 and h2c in Apache?

Viewed 2336

Why does the HTTP Upgrade: header contain both h2 and h2c in Apache?

I think you can only run the HTTP/2 clear text variant, (h2c).

Why does it also include the options h2? Since it is only going to be accessed from TLS, right?

Edit, final question:

Over HTTP, Apache ignores request to upgrade to h2, but it usually advertises it.

Client (HTTP/1.1 without the upgrade header) => Apache (Sends Upgrade: h2, h2c)

Client (HTTP/1.1 with Upgrade: h2) => Apache (Ignores request to upgrade,
                                              and responds back with HTTP/1.1)

Over HTTP, Apache respects upgrade to h2c, and it usually advertises it :)

Client (HTTP/1.1 without the upgrade header) => Apache (Sends Upgrade: h2, h2c)

Client (HTTP/1.1 with Upgrade: h2c) => Apache (Respects request, sends
                                               101 Switching Protocols)
                                       Apache (Uses HTTP/2 Cleartext)

Over HTTPS, Apache ignores all requests to upgrade to h2 and h2c. And Apache also sends h2c over HTTPS. Why is this?

Is all the above correct behavior according to the standards?

1 Answers

Because you can upgrade to h2 or h2c. Yes the former would be negotiated automatically if you used HTTPS initially, but if you didn’t then it’s valid to say you can use h2 if you do switch to that. You could also send back a 301/302 redirect which would effectively redirect you to HTTPS and so h2 but since the upgrade mechanism is not used by browsers maybe you can’t assume the sending application will use those?

A bigger question to me is why the upgrade mechanism exists at all! As I think you’re implying, if you can talk HTTPS then you’ll be using HTTP/2 if you can and so don’t need the upgrade mechanism. And the reason browsers only support HTTP/2 over HTTPS is its so unreliable over HTTP as middleboxes assume HTTP/1 - which means the only use of h2c is for non-browser communication where you’re basically in control of the end to end connection so can skip the upgrade dance and go straight the h2c (aka the “prior knowledge” method of using HTTP/2). The preface message can always be used to reject and/or fallback to HTTP/1 if a client unexpectedly doesn’t support HTTP/2.

In the meantime the upgrade mechanism does cause real problems. For example nginx used to blindly forward this on even if it was already using HTTP/2 to the client. So if a backend Apache server saw the nginx connection was HTTP/1.1 and then suggested HTTP/2 then nginx forward that on to the browser which gets confused as it’s already talking HTTP/2 (to nginx). Safari used to handle this really badly and break and refuse to display the site basically. Yes maybe if Apache hadn’t suggested h2 in the upgrade header this wouldn’t have happened (is that where your question stems from?) but technically it’s nginx at fault here. I can’t remember if they fixed this but think they did.

Anyway, this has been recognised by the HTTP Working Group and they are likely to remove the upgrade mechanism for the next update to the HTTP/2 spec.

While we’re at it, I do wonder if there’s much use for h2c? Obviously the browsers not supporting it puts a severe dent in its usage but, as I say, that was go good reason. Is it used much in non-browser contexts? Difficult to say and even if it is would the requirement to use HTTPS be that much of an extra burden and/or be more reliable?

Edit: to answer your further questions.

Yes all of that is correct.

Case 1: You cannot use h2 of HTTP so it cannot be used via the upgrade header. It’s arguable whether h2 should be in the upgrade header for this reasons and, as you point out, it’s not listed in the IANA registry of allowed upgrade headers. My understanding is this is for upgrading a connection over the same transport protocol and while h2 would still be over HTTP over TCP, so technically over the same transport protocol, it does need HTTP so does need a new connection (and usually different port) so definitely stretching the definition of upgrade!

Case 2: Yes this is what should happen.

Case 3: Arguable but I think technically correct. I’d call that a downgrade rather than an upgrade, but the upgrade mechanism is really a “I support different protocols you might prefer” mechanism rather necessarily an “upgrade to a better protocol” header. Similar to case 1 this will likely require a new connection so again it’s arguable whether this really fits in with the upgrade mechanism as it was intended to be used.

As I say all this is fairly irrelevant, or will be soon, as it will be removed from the HTTP/2 spec.

Related