How to Generate a Sitemap for Your Phoenix App

You've got your app built with Phoenix Framework now you want to generate a sitemap for it.

Googling one can find ways to generate a sitemap dynamically by creating a route/controller just for that.

However, in this post, I am gonna show you another approach: generating a sitemap via script and serving it statically.

Install sitemap

Add sitemap to your list of dependencies in mix.exs:

def deps do
  [{:sitemap, "~> 1.1"}]
end

Ensure sitemap is started before your application:

def application do
  [extra_applications: [:sitemap]]
end

Run mix deps.get.

For further instructions check their readme.

Create the script

Let's create the script responsible for generating the sitemap:
# lib/your_app/sitemap.exs
defmodule YourApp.Sitemap do
  alias YourApp.{Repo, Product}
  alias YourAppWeb.{Endpoint, Router.Helpers}

  use Sitemap, compress: false,
    host: "https://#{Application.get_env(:your_app, Endpoint)[:url][:host]}",
    files_path: "priv/static/sitemaps/"

  def generate do
    create do
      for product <- Repo.all(Product) do
        add Helpers.product_path(Endpoint, :show, product.slug),
          lastmod: product.updated_at, changefreq: "daily"
      end

      ping()
    end
  end
end

YourApp.Sitemap.generate

I set compress: false so it's easier to check the outcome, you can change it if you want.

Notice that I am iterating over all products from the database and generating a xml node for each one.

Set host in prod.exs

In order to have the proper host in get_env above, you need to set it in prod.exs like:

config :your_app, YourAppWeb.Endpoint,
  url: [host: "yourapp.com", port: 80],
  cache_static_manifest: "priv/static/cache_manifest.json"

Allow serving of sitemaps

In the script we have files_path: "priv/static/sitemaps/", this means the sitemap will be generated inside this directory. We need to tell Phoenix to allow serving this folder in lib/your_app_web/endpoint.ex:

plug Plug.Static,
  at: "/",
  from: :your_app,
  gzip: false,
  only: ~w(css fonts images js favicon.ico robots.txt sitemaps)

Run the script

You can run the script with mix run lib/your_app/sitemap.exs, but if you want to run it on production, you must set MIX_ENV=prod. I like to create a make task for it:

# Makefile
generate_sitemap:
  MIX_ENV=prod mix run lib/your_app/sitemap.exs

Automate script run

If you are like me and use dokku for running and deploying your application, you can run the make task above every time you make a new deploy by having app.json like:

{
  "scripts": {
    "dokku": {
      "predeploy": "make generate_sitemap",
      "postdeploy": "mix ecto.migrate"
    }
  }
}

This way, we always generate the sitemap before deploying the newer version.

That's all.

Written on December 28, 2020

Share: