I recently went through some pain configuring an Elixir Phoenix application to run properly behind an Nginx proxy. Moreover, my app is using LiveView which seems to complicate further the situation. There are a few detailed discussions on the subject like:
https://elixirforum.com/t/phoenix-application-behind-aws-alb/49203
but it took me a while to figure out what I'm missing so this post will hopefully explain how to achieve that and clarify some of the built-in configurations you can use to make it work,
The problem
I have a Phoenix application that is part of a bigger project that I needed to put on a subdirectory path of the main application - the main application is running on example.com and I needed to configure my Phoenix app to run on example.com/app. I have used the following piece of Nginx code to achieve that in the past (don't copy-paste that just yet):
What is Nginx?
Nginx is a high-performance web server and reverse proxy that efficiently handles HTTP, HTTPS, and other network protocols. It is widely used to serve and distribute web content, as well as improve website performance and scalability.location /app {
proxy_pass some-address/ip;
proxy_ssl_server_name on;
}
so that all traffic on example.com/app
is routed to my Phoenix application (more info on this here: https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/).
This however creates a problem as all the internal Phoenix routes are relative to the root path.
Here are the steps to make this work:
1. The Endpoint :path
option
The Phoenix endpoint offers an option named :path
that according to the documentation (https://hexdocs.pm/phoenix/Phoenix.Endpoint.html), seems to be exactly what is needed:
The :path option can be used to override root path. Useful when hosting Phoenix behind a reverse proxy with URL rewrite rules
This config looks like this:
config :app, AppWeb.Endpoint,
path: "/app"
However, configuring the :path
option to /app
did not make sense to me at first as the prefix /app
was getting prepended to all the generated internal links but not to the actual URL path. In other words, all links became /app/users
, while the URL paths remained like /users
...
2. The correct Nginx config
Then I realized that :path
configuration expects the proxy configuration to be routing all traffic to the root of the Phoenix app and stripping off the path it's mounted on. For this, one only needs a slash at the end of nginx path like this:
location /app/ {
proxy_pass some-address/ip;
proxy_ssl_server_name on;
}
3. Static paths and the :at
option
This far I've solved the problem with the general paths across the application but the assets would still be pointing to incorrect paths. Fixing this consists of two steps:
a. Plug.Static :at
option
First one needs to setup the :at
option for the Plug.Static config (https://hexdocs.pm/plug/Plug.Static.html). In my case, I had to set the /app
value like this:
plug Plug.Static,
at: "/app"
This would result in Nginx routing traffic to the root of the application and not to the route that was originally called.
Layout path
Once the :at
option is set, the corresponding static paths need to be updated as well:
Routes.static_path(@conn, "/app/assets/app.js")}
4. Fixing LiveView
What is LiveView?
Phoenix LiveView is a real-time, server-rendered framework for building interactive functionalities without the need for JavaScript.The steps above should get a basic Phoenix app running but aren't enough to have LiveView working properly. A few extra steps are needed to make socket connections work:
Nginx
First, in order for Nginx to properly route web socket connections there are three extra lines of configuration needed:
location /app/ {
proxy_pass some-address/ip;
proxy_ssl_server_name on;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Once Nginx properly routes socket requests, the only thing left is updating two paths to include the proxy route:
Endpoint socket config
To reflect the proxy route, there's an update needed in endpoint.ex
. By default, there would be a line that looks like this:
socket "/live", Phoenix.LiveView.Socket....
The make LiveView work, the proxy route needs to be added to the path:
socket "/app/live", Phoenix.LiveView.Socket....
app.js config
And the final step, in the app.js file, the proxy path needs to be added:
let liveSocket = new LiveSocket("/app/live", ...
And that's it! There are all the steps required to make a Phoenix application with LiveView work behind a proxy.