So HTTP/2 is the next generation of good-old HTTP protocol, derived from Google’s experimental SPDY protocol. It’s history, design goals and implementation details are well described in a great post by Ilya Grigorik. Turns out that HTTP/2 protocol is well supported by modern browsers.
The Phoenix community is heavily working on bringing HTTP/2 support into the framework. Few days ago they announced that the Plug library, which is one of the building blocks underneath the framework, got 1.5.0 RC-1 update. It brings all required plumbing for HTTP/2 and is stable enough to start testing it on a development setup. Another core part of any Phoenix application is Cowboy HTTP server. Thanks to awesome Loïc Hoguin who develops it, the 2nd version of Cowboy fully supports HTTP/2.
The update path is pretty straightforward.
1. Adjust `mix.exs` existing dependencies so that they now point to the newest version of libs:
defp deps do
[{:phoenix,git: "https://github.com/phoenixframework/phoenix", branch: "master", override: true},
{:plug, "1.5.0-rc.1", override: true},
{:cowboy, "~> 2.1", override: true}
...
]
end
2. Run mix deps.get
to fetch new dependencies.
3. Since HTTP/2 works only with TLS encryption, we have to generate self-signed certificate:
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -keyout priv/server.key -out priv/server.pem
4. Switch handler and turn on HTTPS support in config/dev.exs
:
config :yourapp, YourAppWeb.Endpoint,
handler: Phoenix.Endpoint.Cowboy2Handler,
https: [port: 4001, keyfile: "priv/server.key", certfile: "priv/server.pem"]
5. That’s it. Now restart phx.server
and visit secure https://localhost:4001
URL to check if it works properly. The first time you enter it the browser will tell you that connection is not secure and the certificate is not issued by a certificate authority. Just add this URL to exception list and off we go. You’ll see H2 protocol in DevTools:

6. In addition that would be great, when someone visits conventional phoenix’s 4000 port on localhost via HTTP, to automatically switch protocol to secure HTTPS. To do that change endpoint configuration this way:
config :yourapp, YourAppWeb.Endpoint,
http: [port: 4000],
url: [scheme: "https", host: "localhost:4001"],
debug_errors: true,
code_reloader: true,
check_origin: false,
handler: Phoenix.Endpoint.Cowboy2Handler,
https: [compress: true,
port: 4001,
keyfile: "priv/server.key",
certfile: "priv/server.pem"],
force_ssl: [hsts: true]
Cowboy server will listen not secure requests on 4000 port and when HTTP request comes in it’ll force a browser to redirect to HTTPS protocol, to 4001 port with HTTP Strict Transport Security (HSTS) turned on.
One thing which appears partially broken in Nots after this update is Phoenix.ConnTest. This module is made up for testing controllers like this:
build_conn()
|> put_req_header("accept", "application/json")
|> get("/")
Under the hood, the get
method delegates connection creation to Plug.Adapters.Test.Conn
which uses http scheme by default when URI like “/“ with no scheme and host comes in (as in the example above).

In order to force Plug to use correct HTTPS protocol and right port, the scheme and port should be provided in a URI, e.g. https://localhost:4001/
.
URI module from Elixir standard distribution is made just for that. The make_full_url
helper function adds scheme and host according to the configuration file setup:
defp make_full_url(url) do
URI.parse(url)
|> Map.put(:scheme, YourAppWeb.Endpoint.config(:url)[:scheme])
|> Map.put(:host, YourAppWeb.Endpoint.config(:url)[:host])
|> URI.to_string
end
build_conn()
|> put_req_header("accept", "application/json")
|> get(make_full_url("/"))
Now everything works smoothly, and a little bit faster due to HTTP/2 stream multiplexing.