Fly.io, LiveView, and custom domains
How to configure a Fly.io deployed Phoenix LiveView application with a custom domain
Fly.io + Custom Domains + LiveView configurations
Getting a basic Phoenix application deployed on Fly.io is dead simple if you follow Elixir getting started guide.
However, if you end up configuring a custom domain for your application, the configuration for the application prevents Websocket connections when you visit the application at the custom domain.
Thankfully resolving this is dead simple and there’s several approaches you can take!
Option 1: Update fly.toml
The default configuration for a Fly.io deployed application uses the PHX_HOST
env variable to build the url
for the application’s Endpoint. Combined with
the Endpoint defaulting to check_origin: true
it results in all connections
with an origin differing from the configured url
to be blocked.
Changing the PHX_HOST
value in your fly.toml
to be the custom domain and
deploying the change will allow connections from the custom domain. This will
prevent any other domains from connecting successfully, including the Fly
provisioned domain, but is simple and quick.
Option 2: Adjust the Endpoint’s check_origin
configuration
Adjusting the Endpoint’s check_origin
will allow specifying a list of domains
that will be permitted to make websocket connections.
As per the
docs
the check_origin
can be set to a list of explicitly allowed origins.
So doing an update like so will allow both the Fly provisioned url and the custom domain.
# config/runtime.exs
host = System.get_env("PHX_HOST") || "example.com"
port = String.to_integer(System.get_env("PORT") || "443")
config :your_application, YourApplicationWeb.Endpoint,
check_origin: [
# Allow the Fly provisioned url to connect
"https://#{host}:#{port}",
# Allow your custom domain to connect at any subdomain
# or at the apex.
"//yourcustomdomain.com:#{port}",
],
url: [host: host, port: port, scheme: "https"],
# ...
This is a tidy solution that explicitly defines the allowed URLs but requires manual updates any time you wish to provision a new domain.
Option 3: Allow any origins that match the current request’s host
A third option allows for the addition of any custom domains without needing configuration or code changes.
This requires adjusting the Endpoint’s configuration check_origin
as
well as the force_ssl
option.
Setting the Endpoint’s check_origin: :conn
will accept any origin matching the
request connection’s host, port, and scheme.
This can be combined with the force_ssl
option to ensure the correct handling
of Fly provisioned domains as well as custom domains (and ensure SSL is used as
an added bonus).
Similar to option 2, the config/runtime.exs
needs updating and, since the
force_ssl
is a compile time option, the config/prod.exs
.
There’s a couple settings to update:
-
hsts: true
: declares the site is accessible via HTTPS only. -
host: nil
: allows dynamic redirects to the host of the request enforcing HTTPS on the fly domain and any custom domains added -
rewrite_on: [...]
: specifies the headers to use when rewriting http to https
Putting this all together results in the following:
# config/runtime.exs
host = System.get_env("PHX_HOST") || "example.com"
port = String.to_integer(System.get_env("PORT") || "443")
config :your_application, YourApplicationWeb.Endpoint,
check_origin: :conn,
url: [host: host, port: port, scheme: "https"],
# ...
# config/prod.exs
config :your_application, YourApplicationWeb.Endpoint,
force_ssl: [
hsts: true,
host: nil,
rewrite_on: [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto]
],
# ...
Summary
Deploying Phoenix applications on Fly.io is a breeze but there are certain things, like adding a custom domain, that can cause small issues when deployed with the default configuration.
Hopefully this article illustrates how to get websocket connections working with a custom domain on Fly.io.
If you have alternatives, or adjustments, or any thoughts at all, feel free to open an issue in this website’s repo.
2024-08-16