How to configure an Elixir Phoenix app with LiveView to run behind an NGINX proxy


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

https://elixirforum.com/t/configure-asset-paths-on-a-phoenix-app-that-runs-on-a-domain-namespace/56704

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.

Need help?

Book a 1h session with an expert on this very matter

€75/h

Pair programming

Pair programming is an agile software development technique in which two programmers work together at one workstation. One, the driver, writes code while the other, the observer or navigator,[1] reviews each line of code as it is typed in. The two programmers switch roles frequently.