We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Good News/Bad News
Good news: Fly.io makes it really easy to deploy Phoenix apps and configure them to use custom domains and provision SSL certificates.
Bad news: If your app also has LiveView pages, until you make configuration changes, it will throw errors as soon as you load a LiveView when using your custom domain URL.
[error] Could not check origin for Phoenix.Socket transport.
That’s the relevant error in your Fly.io logs. (fly logs
is the command from your terminal to see your fly.io logs.)
On your LiveView page (in Phoenix 1.7.6/LiveView 0.19.0), the error presented to the user is less helpful:
We can’t find the internet.

The source of this error is security–specifically Phoenix and Fly.io ensuring that the “live” socket connection used to provide instant updates to your LiveView page is secure. Another way to think about this is as another flavor of cross-origin resource sharing (CORS) protection, which is what all web apps (whether using deadviews or liveviews) must do to prevent “man-in-the-middle” security attacks.
More precisely, this error happens because our custom url (elixirized.com in our case) is not the same as the internal fly.io URL assigned to our app, so we need to change our app’s configuration so that security validation will allow that socket connection to our LiveView pages via our custom domain.
-
In my humble opinion, the otherwise-excellent Fly.io
flyctl certs create
process described here should make the necessary changes for us. - This gap is compounded by the lack of obvious documentation about the required configuration changes to make custom domains pass these security checks.
Required Configuration Changes
As of the Phoenix/LiveView versions listed above, the changes need to be made are to:
To config/runtime.exs
To the config :your_app_name, YourAppNameWeb.Endpoint,
logic, ADD:
check_origin: :conn,
To config/prod.exs:
To the config :your_app_name, YourAppNameWeb.Endpoint,
logic, ADD:
force_ssl: [rewrite_on: [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto]],
To give you the complete picture (sadly missing in so many tutorials), the resulting files will look like this:
Updated config/runtime.exs
import Config
# (boilerplate comments removed for brevity)
if System.get_env("PHX_SERVER") do
config :your_app_name, YourAppNameWeb.Endpoint, server: true
end
if config_env() == :prod do
# (boilerplate comments removed for brevity)
secret_key_base =
System.get_env("SECRET_KEY_BASE") ||
raise """
environment variable SECRET_KEY_BASE is missing.
You can generate one by calling: mix phx.gen.secret
"""
host = System.get_env("PHX_HOST") || "your-custom-domain.com"
port = String.to_integer(System.get_env("PORT") || "4000")
config :your_app_name, YourAppNameWeb.Endpoint,
check_origin: :conn,
url: [host: host, port: 443, scheme: "https"],
http:
[
# (boilerplate comments removed for brevity)
ip: {0, 0, 0, 0, 0, 0, 0, 0},
port: port
],
secret_key_base: secret_key_base
# (boilerplate comments removed for brevity)
end
Updated config/prod.exs
import Config
# (boilerplate comments removed for brevity)
config :your_app_name, YourAppNameWeb.Endpoint,
force_ssl: [rewrite_on: [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto]],
cache_static_manifest: "priv/static/cache_manifest.json"
# Configures Swoosh API Client
config :swoosh, api_client: Swoosh.ApiClient.Finch, finch_name: Elixirized.Finch
# Do not print debug messages in production
config :logger, level: :info
# Runtime production configuration, including reading
# of environment variables, is done on config/runtime.exs.
So there you have it. Literally two lines of code to change.
- May the time this saves you make up for the hours of head-bashing we went through to figure this out.