How to serve multiple website files on the same server from Cowboy

Hi all! I am trying to point multiple domain’s DNS records to my private server.

This server will be running cowboy which will, hopefully, grab the hostname included within the request and serve the appropriate website files.

However, I am having a heck of a time trying to get css/js to deliver consistently–it 404’s constantly.

For context, here is the file structure:

priv/
  -sites/
     --localhost/
       ---static/
         ----assets/
           -----css/
           -----js/
       ---templates/
         ----default/
              -----home.html
         ----reader/
              -----home.html
         ----viewer/
              -----home.html

And then the code responsible for routing:

init([]) ->
    {ok, Cwd} = file:get_cwd(),
    StaticPath = filename:join([Cwd, "priv", "sites", "localhost", "static"]),
    Dispatch = cowboy_router:compile([
    {'_', [
        {"/", page_handler, []},
        {"/static/assets/[...]", cowboy_static,   % Change to match exact path
            {priv_dir, StaticPath ++ "/assets",        % Use exact directory
             [{mimetypes, cow_mimetypes, all}]}},
        {"/page/:id", page_handler, []}
    ]}
]),

    {ok, _} = cowboy:start_clear(
        http_listener,
        [{port, 8080}],
        #{env => #{dispatch => Dispatch}}
    ),

    {ok, {{one_for_one, 5, 10}, []}}.

It appears as if the main issue is that I cannot get access the requested hostname and, instead, have had to hardcode localhost… which doesn’t allow me to serve multiple site files like I want.

I could also be approaching this totally wrong, I’m new and trying, so please be nice haha

1 Like

You’d have to write your own ‘static’ handler wrapping the cowboy_static callbacks if you want ‘dynamic’ paths. See serve static files based on path parameter · Issue #1556 · ninenines/cowboy · GitHub

The paths beyond the hostname would be the same, the only thing that has to change is the hostname for each request that comes in. Does your suggestion change with this information?

If all hostnames are known in advance you’d have a static handler for each path.

If it needs to be dynamic, not static, and you want to use the static handler you’d need to wrap that in order to dynamically construct the base paths.

You could use a normal handler and do all the file reading, mine type handling yourself, but it would be easier to do the former.

if you use priv_dir the arguments are a little bit different. You supply the name of your app and a path relative to the priv/-directory (documentation).

In your case this would be (replace my_app with the name of your app):

        {"/static/assets/[...]", cowboy_static, {priv_dir, my_app, "sites/localhost/static/assets"}}

assuming you want to access it using http://localhost:8080/static/assets/js/foo.js.
Like LeonardB said, if your hosts are static you can just use the host-handling of cowboy. Currently you use '_' to make clear that you don’t mind the host. If you know the hosts beforehand you can let Cowboy handle it for you:

{"localhost", [{"/static/assets/[...]", cowboy_static, {priv_dir, my_app, "sites/localhost/static/assets"}}]},
{"foo", [{"/static/assets/[...]", cowboy_static, {priv_dir, my_app, "sites/foo/static/assets"}}]},

Assuming you have the same hierarchy for the host foo in your priv-directory.

(I never get the routes right the first time :slight_smile: this is all written in this comment box, and I did not try it out in erlang itself).