The Usefulness of Phoenix's static_path/2

By Eric Lathrop on

I'm working on a project where I'm building an Elixir Phoenix website to take over specific paths on a larger website. The sites are hosted on AWS, so my plan is to use an ELB with Listener Rules to send requests for specific path prefixes like /news* to my Phoenix server, and send any remaining paths to the old server.

In a default Phoenix website, static assets are put in /priv/static in the file system, and will be served from / on the web. For example the CSS file at /priv/static/css/app.css will be served at /css/app.css. The problem this creates in my situation is that there are several folders for static assets (like /css and /js) which increases the chance of a path conflict with the old server. To fix this, I want to serve all static assets from the web-facing path /static, which allows me to send all requests for static assets in my Phoenix app to my Phoenix server with a single ELB Listener Rule.

In a default Phoenix layout file, you'll see a line like this:

<link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">

static_path/2 is an automatically generated helper that lives in your MyAppWeb.Router.Helpers module. static_path/2 will return the web-facing path for a given file system path under /priv/static. You can configure the path returned from static_path/2 by setting static_url in your endpoint's configuration. I updated my configuration to look like this:

config :my_app_web, MyAppWeb.Endpoint,
  static_url: [path: "/static"],

The above causes static_path/2 to return /static/css/app.css for the file found at /priv/static/css/app.css. You still have to setup Plug.Static to actually serve the file at that path! I had to update the at setting in MyAppWeb.Endpoint like this:

    at: "/static",
    from: :my_app_web,

After this change, everything that uses static_path/2 will generate web-facing paths under /static that actually work. Now you just need to make sure that every HTML template that references a static asset uses static_path/2.

CSS and Relative Paths

The remaining problem is that my CSS file referred to several images for background images like background: url(/images/searchwrapper-bg.png) no-repeat; which no longer work. Because the CSS file and the image file are both static assets, I just switch from absolute to relative URLs like this: background: url(../images/searchwrapper-bg.png) no-repeat;. This works because relative paths in CSS are relative to the web-facing path of the CSS file, and not the current page.

Having fixed these issues, I can quickly update the path where my static assets are being served from.