Dynamically overriding static_url based on clients Host header

I have a Phoenix application that should be accessible in the local network and remotely via a reverse proxy. This means I can’t configure a URL in the Endpoint config, since the client did either use something like hostname.local or reverseproxy.example.tld.

I can easily override the Router URL with a simple Plug that calls Phoenix.Controller.set_router_url/2:

def set_router_url_by_host_header(conn, _opts) do
  allowed_hosts = Application.get_env(:my_app, :allowed_hosts, [])
  requested_host = conn.host

  if requested_host in allowed_hosts do
    Phoenix.Controller.put_router_url(conn, requested_host)

This can’t be done for URLs generated with Routes.static_path though, since they always use the url or static_url configured in the Application env.

As far as I can see there is nothing to override this behavior with a plug, or am I missing something here?

Related StackOverflow question: https://stackoverflow.com/questions/52091185/how-can-i-configure-phoenix-to-use-the-clients-host-header-as-a-base-url-for-sta


A Phoenix application can have more than one Endpoint. Can you use one for each of the domains you are supporting? You will have to run the endpoints on different ports, but you should be able to use the reverse proxy on the remote network to hide that.

1 Like

I’d still be curious about a non static option to have the router helpers just use the incoming host (if used with a conn not just an endpoint). Sometimes there’s just the need for a site work under whatever address I can be reached, no matter what IP or domain it might be.

The first argument to static_url is a struct, that can either be a connection struct or a URI struct. So the trick is to build a URI struct with the information of the domain and pass it to static_url instead of the conn.

I knew this is the case for the path helpers, but it seems to be missing for the static path helpers. Probably because it needs to know where to mount static assets from the endpoint itself.

But I just notices if the domain is really unknown just using a plain path should really be the solution.

1 Like

Oh, I see. Good point. :thinking:

Something like Phoenix.Controller.put_static_url would solve my problem easily. Then I can simply override the URL just like the router URL.

Why not use static_path instead of static_url in this case @Strayer? What would be your use-case of complete urls instead of just a path? (not that I would be opposed to that kind of flexibility)

Because static_path generates full URLs with the configured domain name, that is the problem. See the linked StackOverflow question in my original post for a detailed example.

I am wondering because when I run a vanilla phoenix installation using mix phx.new I get an application which is using static_path and even though it is configured for prod with a url pointing to example.com, the HTML output still contains an absolute path without a domain name.
When I add static_url to the templates though, a full url is generated instead including the configured domain.

Have a look at the example code over here.
It basically is the phoenix app generated by default which has two additions within lib/dingen_web/templates/layout/app.html.eex. I added two image tags there (with the logo). One with static_path and one with static_url.

When in development mode, this gives me the following html (in dev no url is configured in config/dev.exs so the html points to the inferred localhost):

        <img src="/images/phoenix.png" alt="Phoenix Framework Logo"/>
        <img src="http://localhost:4000/images/phoenix.png" alt="Phoenix Framework Logo"/>

When running in production mode (after a quick mix phx.digest), the url configured in config/prod.exs is used:

        <img src="/images/phoenix-5bd99a0d17dd41bc9d9bf6840abcc089.png?vsn=d" alt="Phoenix Framework Logo"/>
        <img src="http://example.com/images/phoenix-5bd99a0d17dd41bc9d9bf6840abcc089.png?vsn=d" alt="Phoenix Framework Logo"/>

If you are experiencing different results in your project, would you mind sharing some example code?