Categories
Golang

Golang: How to Serve favicon, sitemap.xml, and ads.txt?

In this short post, I want to provide ready-to-use code snippets for serving the favicon, sitemap.xml, and ads.txt files.

Hello, world! In this short post, I want to provide ready-to-use code snippets for serving the favicon, sitemap.xml, and ads.txt files. These are a pretty standard set of files for web projects.

For all these examples, I will use standard net/http combined with gorilla/mux library.

How to serve favicon?

First, you need to create and locate a favicon under your resource files. For me it looks like this:

favicon in my project structure
favicon in my project structure

Once you have this file in your project structure, this is the code to serve it:

package website

import (
   "fmt"
   "log"
   "net/http"
   "time"

   "github.com/gorilla/mux"
)

type Website struct {
   DB *storage.Storage
}

func (ws *Website) Start() error {
   r := mux.NewRouter()

   r.HandleFunc("/sitemap.xml", ws.sitemapHandler)
   r.HandleFunc("/favicon.ico", faviconHandler)
   r.HandleFunc("/ads.txt", adsHandler)

   srv := &http.Server{
      Handler: r,
      Addr:    ":8081",
      // Good practice: enforce timeouts for servers you create!
      WriteTimeout: 15 * time.Second,
      ReadTimeout:  15 * time.Second,
   }

   return srv.ListenAndServe()
}

func adsHandler(w http.ResponseWriter, r *http.Request) {
   http.ServeFile(w, r, "resources/fe/website/ads.txt")
}

func faviconHandler(w http.ResponseWriter, r *http.Request) {
   http.ServeFile(w, r, "resources/fe/website/favicon.ico")
}

Please note that this code is the extract from the actual project, so I had to remove some unnecessary parts. The parts that we really interested in are:

r := mux.NewRouter()

This initializes a new gorulla/mux router. Then:

r.HandleFunc("/favicon.ico", faviconHandler)

Using this router we register a new handler mapped to /favicon.ico. And at the end, we use the following function to serve the actual file:

func faviconHandler(w http.ResponseWriter, r *http.Request) {
   http.ServeFile(w, r, "resources/fe/website/favicon.ico")
}

In here, I use the standard library, and it’s .ServeFile function.

How to serve ads.txt?

If you are running any ads on your website then you are familiar with the ads.txt file. This file is used to provide some website specific information to your ad network.

In terms of the codebase, it looks exactly the same as for favicon.ico. You need to locate the file in your directory and then follow a similar code structure:

// register handler mapped to /ads.txt
r.HandleFunc("/ads.txt", adsHandler)

// serve ads.txt as a file
func adsHandler(w http.ResponseWriter, r *http.Request) {
   http.ServeFile(w, r, "resources/fe/website/ads.txt")
}

The very similar approach can also be used for serving robots.txt.

How to serve sitemap.xml?

For serving the sitemap.xml I use a bit different approach. Registering the router and handler is the same:

r := mux.NewRouter()
r.HandleFunc("/sitemap.xml", ws.sitemapHandler)

But, the implementation of the ws.sitemapHandler is a bit different.

What I mean by different? You see, one way of serving the sitemap.xml is very similar to the above explained approach:

  1. You generate the sitemap.xml and save it in your directory.
  2. You serve the sitemap.xml using the same approach as for favicon and ads.txt.

But in my case, I prefer to generate sitemap on each request (at least for now). That’s way my algorithms looks like this:

  1. I load all the items which need to be in sitemap.xml from the storgage.
  2. Using the encoding/xml, I generate a sitemap xml data.
  3. I serve this data with the content type application/xml.

Here is the code:

package website

import (
   "encoding/xml"
   "fmt"
   "log"
   "net/http"
)

type urlset struct {
   XMLNS string `xml:"xmlns,attr"`
   URLs  []url  `xml:"url"`
}

type url struct {
   Loc      string `xml:"loc"`
   LastMod  string `xml:"lastmod"`
   Priority string `xml:"priority"`
}

func (ws *Website) sitemapHandler(w http.ResponseWriter, r *http.Request) {
   articles, err := ws.DB.Articles().ListAllArticles()
   if err != nil {
      errStr := fmt.Sprintf("failed to the article from storage: %v", err)
      log.Println(errStr)
   }

   sitemap := urlset{
      XMLNS: "http://www.sitemaps.org/schemas/sitemap/0.9",
   }
   for _, article := range articles {
      sitemap.URLs = append(sitemap.URLs, url{
         Loc:      fmt.Sprintf("https://codekn.com/article/%d", article.ID),
         LastMod:  article.Posted.Format("2006-01-02"),
         Priority: "1",
      })
   }

   x, err := xml.MarshalIndent(sitemap, "", "  ")
   if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      return
   }

   w.Header().Set("Content-Type", "application/xml")
   w.Write(x)
}

For the big scale project, this is not the best way of serving the sitemap.xml. You might end uploading and serving a huge amount of data. Also, having the /sitemap.xml which fetches data from the database for each request probably is not the best solution.

But for the small-scale side project, this works. Likewise, it is relatively small refactoring to convert this approach to statically served sitemap, or even caching the response for some time duration.

Follow for Updates

Processing…
Success! You're on the list.

By Kanan Rahimov

Sr. Software Engineer

Leave a Reply