The bottom line is that spec says that “the order in which header fields with differing field names are received is not significant”. But then it goes on to say that it is a “good practice” (and it puts “good practice” in quotes) to order the headers a particular way. So, without strict guidance from the spec about the importance of header ordering, it would be interesting to know if header order caused a problem in practice.
The Plack::Middleware::RearrangeHeaders documentation suggests there is some benefit to strict header ordering: “to work around buggy clients like very old MSIE or broken HTTP proxy servers”
You might wonder what the big deal is— why not just stick to the “good practice” recommendation all the time? The difference can be seen in the benchmarks provided by HTTP::Headers::Fast. By ignoring the good-practice header order, an alternate implementation was able to speed-up header generation to be about twice as fast. Considering that a web-app needs to generate a header on every single request, making header generation smaller and faster is potentially a tangible win, while also still being spec-compliant.
So let’s look at who is doing what:
- CGI.pm does not order headers by default or have an option to do so. CGI.pm is used by default by CGI::Application and also by Mason and Jifty when they run under CGI.
- CGI::Simple predictably models CGI.pm’s behavior
- Dancer generates headers itself, without regard to order
- Plack is a mixed bag. If you generate your own headers, I believe they will be passed through unmodified, but if you use Plack::Request they will be ordered (with no option to disable this). With Plack you also have the option to the the RearrangeHeaders Middleware if you want to be certain that your headers are in the “good practice” order.
- Mojo generates its own headers, always in the “good practice” order, with no way to disable the ordering.
- Catalyst, Plack and others rely on HTTP::Headers to generate headers. HTTP::Headers also currently always generates headers in good-practice order, although there has been some discussion about adding an option to produce the headers in an unsorted order.
- And finally, I found HTTP::Headers::Fast which is used by HTTP::Engine and provides the user equal options to generate headers in a sorted or unsorted order.
- For a bonus consultation, I looked at Rack, a web server written in Ruby sometimes paired with Rails, and one of the inspirations for Plack. It appears that that Rack does not order HTTP headers either.
So which approach is best? I think what makes most sense to me is leave HTTP headers unsorted by default. This complies with the spec and can be accomplished with a simpler implementation and better performance. The good-practice ordering is a nice-to-have option if you know you need it or want the tradeoff.
And should we be worried about header ordering issues in practice? With CGI.pm in use for over a decade, I can’t recall any header-ordering bugs of the hundreds of CGI.pm bugs I’ve triaged in that bug tracker. I’m interested to know if there are any real-world web clients or proxies that require or benefit from the good-practice ordering.