Phoenix route path with suffix (can route be made to look like static .html file?)

I have routes that look like this:

    get("/post/:slug_and_id", PostController, :show)
    get("/post/:slug_and_id/comment/:comment_id", PostController, :comment)

Instead of getting urls like
I’d instead want them to look like this

It feels like I should just be able to do this, but it doesn’t work:

    get("/post/:slug_and_id.html", PostController, :show)
    get("/post/:slug_and_id/comment/:comment_id.html", PostController, :comment)

Is this possible? Perhaps via some custom Plug? Has anyone ever done something like this in their phoenix app?


OK so I tried this out of curiosity, and got this helpful error message:

== Compilation error in file lib/playground_web/router.ex ==
** (Plug.Router.InvalidSpecError) invalid dynamic path. Only letters, numbers, and underscore are allowed after : in "/:something.html"
    (plug 1.15.3) lib/plug/router/utils.ex:89: Plug.Router.Utils.build_path_match/2
    (phoenix 1.7.10) lib/phoenix/router/route.ex:94: Phoenix.Router.Route.build_path_and_binding/1
    (phoenix 1.7.10) lib/phoenix/router/route.ex:67: Phoenix.Router.Route.exprs/2
    (phoenix 1.7.10) lib/phoenix/router.ex:501: anonymous fn/2 in Phoenix.Router."MACRO-__before_compile__"/2
    (elixir 1.15.7) lib/enum.ex:1693: Enum."-map/2-lists^map/1-1-"/2
    (elixir 1.15.7) lib/enum.ex:1693: Enum."-map/2-lists^map/1-1-"/2
    (phoenix 1.7.10) expanding macro: Phoenix.Router.__before_compile__/1
    lib/playground_web/router.ex:1: PlaygroundWeb.Router (module)

The noteworthy part:

** (Plug.Router.InvalidSpecError) invalid dynamic path. Only letters, numbers, and underscore are allowed after : in "/:something.html"

The closest I can get is by doing this in the router:

    # doesn't work
    get "/:something.html", PageController, :home

    # does work
    get "/:something/some_page.html", PageController, :home

So it looks you can have a dynamic path but the period is the tricky period. So the last part would have to look something like this, using your example:

    get("/post/:slug_and_id/post.html", PostController, :show)
    get("/post/:slug_and_id/comment/:comment_id/comment.html", PostController, :comment)

But why in the world would you want to falsely represent a dynamically-generated page with a URL that indicates that it’s just a static HTML file?

Here’s a thread with roughly the same question. The answer is to modify params with a plug:

Though the above is a little old, maybe things are different today.

A guess would be porting over a website which previously had the convention of .html at the end of a route :slight_smile:

1 Like

Thanks for the suggestions!

I think this approach will work for me, but I still need to try it out:

get("/post/*slug_and_id", PostController, :show, slug_and_id: ~r/[^\/]+\.html$/)

Then I have to change my parsing of slug_and_id to take out the .html. Then I’ll also have to adjust all places where I generate links using the path helper to manually append the suffix but that’s not too bad.

As to the why… I have an existing site with 100k+ posts on it that I want to convert into a static site (I no longer allow contributions, so it’s effectively static) to save on costs. This would be a step in that direction and would make it easier for me to use wget or some other method to download a fully static version of the site.


Maybe this library would be helpful?