Phoenix Endpoint Hostname Path Helpers

Overview
I deployed a Phoenix app and I’m using the Phoenix path_helpers in situations when I want to write the full URL like this.

<%= Routes.page_url(MyappWeb.Endpoint, :index) %>

Problem
It’s returning the generated fly app name http://foo-acme-700.fly.dev:443 as the url with port 443 but I would prefer it would return my domain name www.example.com.

Question
Where do I make the changes so that my path_helpers is using the domain name I own and not the fly generated app name?

Option A - Do I edit the fly.toml ?

# Remove PHX_HOST = "foo-acme-700.fly.dev"
PHX_HOST = "example.com"

Option B - Do I Add this in config/prod.exs

config :myapp, MyappWeb.Endpoint,
      http: [port: {:system, "PORT"}],
      url: [host: "example.com"]

Option C - Do I Add something in config/runtime.exs

# I don't know what changes I would make

Any feedback or direction on documentation from fly.io explaining how to do this for a Phoenix 1.6.5 application would be appreciated.

Yep! Edit fly.toml.

Alternatively, you can remove that URL line and do something like this:

config :myapp, MyappWeb.Endpoint,
      http: [port: {:system, "PORT"}],
      force_ssl: [rewrite_on: [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto]],
      check_origin: :conn

This is more generic, supports any hostname you point at the app, and will hopefully become the default in Phoenix someday.

Hi @kurt

Editing the fly.toml does work. Thank you.

However when I use the the Phoenix path_helpers I get the following:

<%= Routes.page_url(MyappWeb.Endpoint, :index) %>
# This returns
http://www.example.com:443

How do I hide the port number and change the http into https

Re: Alternative.
Your alternative suggestion you provided is this for the prod.exs or runtime.exs file?

The alternative goes into runtime.exs. You can replace the existing endpoint config with that, no URL necessary.

I think that will fix the weird http://www.example.com:443 problem too. You have to teach Phoenix how to detect SSL at the proxy level.

Hi Kurt,

BEFORE runtime.exs

import Config

if System.get_env("PHX_SERVER") && System.get_env("RELEASE_NAME") do
  config :myapp, MyappWeb.Endpoint, server: true
end

if config_env() == :prod do

  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") || "example.com"
  port = String.to_integer(System.get_env("PORT") || "4000")

  config :myapp, MyappWeb.Endpoint,
    url: [host: host, port: 443],
    http: [
      # Enable IPv6 and bind on all interfaces.
      # Set it to  {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
      # See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
      # for details about using IPv6 vs IPv4 and loopback vs public addresses.
      ip: {0, 0, 0, 0, 0, 0, 0, 0},
      port: port
    ],
    secret_key_base: secret_key_base

end

and now is this what the AFTER runtime.exs should look like:

import Config

if System.get_env("PHX_SERVER") && System.get_env("RELEASE_NAME") do
  config :myapp, MyappWeb.Endpoint, server: true
end

if config_env() == :prod do

  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
      """

config :myapp, MyappWeb.Endpoint,
      http: [port: {:system, "PORT"}],
      force_ssl: [rewrite_on: [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto]],
      check_origin: :conn,
      secret_key_base: secret_key_base

end

I removed/deleted a lot of things from BEFORE runtime.exs and I just want to make sure I have all the necessary information.

Try this instead:

port = String.to_integer(System.get_env("PORT") || "4000")
config :myapp, MyappWeb.Endpoint,
      force_ssl: [rewrite_on: [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto]],
      check_origin: :conn,
      secret_key_base: secret_key_base,
      http: [
        ip: {0, 0, 0, 0, 0, 0, 0, 0},
        port: port
      ]

http needs both the environment variable and the ip value.

Hi Kurt

I put the code you suggested in my runtime.exs and I’m getting this error:

Reaped child process with pid: 544, exit code: 0
[info] 00:35:14.011 [error] :force_ssl mismatch for MyAppWeb.Endpoint.
[info] Compile time configuration: nil
[info] Runtime configuration  : [rewrite_on: [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto]]
[info] :force_ssl is a compile-time configuration, so setting
[info] it at runtime has no effect. Therefore you must set it in your
[info] config/prod.exs or similar (not in your config/releases.exs)

Does this mean I need to put your suggestion in both prod.exs AND runtime.exs

I’m not deploying my with Elixir releases. I don’t know if this matter I just followed the tutorial from flyio documentation

I’m running into the same issue of having my route helpers generate urls of the form http://foo-acme-700.fly.dev:443. To me there are two problems here:

  1. The internal app host name is being used
  2. http and :443 are included in the url

Solutions

The internal app host name is being used

Set your PHX_HOST environment variable in fly.toml to the host you want say www.example.com.

http and :443 are included in the url

I still haven’t figured out the proper solution to the second problem. I’m using a phoenix project that was recently generated. I made the changes and saw that the app didn’t work. There were a handful of errors being thrown.

The latest phx.new generator fixes this issue. You want this in your runtime.exs endpoint config:

config :my_app, MyAppWeb.Endpoint,
  ...,
  url: [host: ..., port: 443, scheme: "https"]
2 Likes