5

In a new Phoenix app the Plug.Head plug is present by default and I was intrigued about its significance.

I know that "the HEAD method is identical to GET except that the server MUST NOT send a message body in the response".

I think the official Phoenix guides are top-notch but this threw me off in the Routing guide:

Plug.Head - converts HEAD requests to GET requests and strips the response body

If HEAD requests are without a body then why is this needed? I thought maybe to rein in malformed requests but looking at the Plug.Head implementation, it just switches the HEAD method to GET.

  def call(%Conn{method: "HEAD"} = conn, []), do: %{conn | method: "GET"}
  def call(conn, []), do: conn
end

The closest thing I was able to find on this topic is a question on ServerFault but it was related to NGINX and a flawed application logic where HEAD requests needed to be converted to GET and the respective GET responses back to HEAD.

Community
  • 1
  • 1
toraritte
  • 6,300
  • 3
  • 46
  • 67

2 Answers2

3

Since Phoenix is largely inspired by Rails, you can safely bet Plug.Head is inspired by Rack::Head.

A HEAD request returns the same response as GET but with headers only. So to produce the correct headers, they're routed to GET actions in your Phoenix app.

However to produce the correct (empty) body, the response's body must be stripped. Because Rack::Head is middleware, it gets to do so after it gets the response from controllers.

In contrast, Plug's architecture works more like a pipeline, Plug.Head modifies the method and passes conn along, but never sees it again.

If you see cdegroot's answer, the responsibility to strip the response's body is passed to the Plug.Conn.Adapter to implement (i.e. the webserver).

Community
  • 1
  • 1
Jay Jun
  • 221
  • 2
  • 6
  • So the simple answer is that **the HEAD -> GET conversion is to make route-matching easier?** I tested a simple Phoenix app with `curl` HEAD and GET requests and the responses are correct and the logs show HEAD request correctly (although this is not surprising as `Plug.Logger` precedes `Plug.Head`). So I guess that the initial request method is retained somewhere but this would be material for another question. – toraritte Feb 05 '17 at 01:46
2

AFAICT, the idea is that Plug.Head just ensures that the request is processed as a GET; the second part of implementing HEAD, which is not sending a body, is done by the Plug connection adapters. The documentation for most callbacks, like send_resp, specifies that "If the request has method "HEAD", the adapter should not send the response to the client."

cdegroot
  • 1,765
  • 11
  • 12
  • Thanks for pointing to the connection adapters! Although the cited comment is either inaccurate or I am still far from the big picture because I kind of miss the word "**body**" from "the adapter should not send the response **body** to the client". The response is sent independently of the request method, only the body is omitted from a response to a HEAD. But then again I maybe misinterpreting how adapters work. – toraritte Feb 05 '17 at 01:57
  • The comment is correct. Servers should not send a body in **response** to a HEAD **request**. It's irrelevant whether the HEAD **request** itself has a body. – Jay Jun Feb 05 '17 at 05:36