Go Open Graph Meta Tags: Add OG to Your Go Web App

How to add og:title, og:image, og:description, and Twitter card meta tags in Go — using net/http, html/template, Gin, Echo, or Fiber.

Standard library: net/http + html/template

// main.go
package main

import (
	"html/template"
	"net/http"
)

type PageData struct {
	Title       string
	Description string
	OGImage     string
	CanonicalURL string
}

var tmpl = template.Must(template.New("page").Parse(`<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{.Title}}</title>
  <meta name="description" content="{{.Description}}">
  <meta property="og:title"        content="{{.Title}}">
  <meta property="og:description"  content="{{.Description}}">
  <meta property="og:image"        content="{{.OGImage}}">
  <meta property="og:url"          content="{{.CanonicalURL}}">
  <meta property="og:type"         content="article">
  <meta name="twitter:card"        content="summary_large_image">
  <meta name="twitter:title"       content="{{.Title}}">
  <meta name="twitter:image"       content="{{.OGImage}}">
  <link rel="canonical" href="{{.CanonicalURL}}">
</head>
<body><h1>{{.Title}}</h1></body>
</html>`))

func blogPostHandler(w http.ResponseWriter, r *http.Request) {
	slug := r.PathValue("slug") // Go 1.22+ path value
	post := getPost(slug)       // your data source
	data := PageData{
		Title:        post.Title,
		Description:  post.Excerpt,
		OGImage:      "https://yourdomain.com/og/" + slug + ".png",
		CanonicalURL: "https://yourdomain.com/blog/" + slug,
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	tmpl.Execute(w, data)
}

func main() {
	http.HandleFunc("/blog/{slug}", blogPostHandler)
	http.ListenAndServe(":8080", nil)
}

Gin framework

// main.go — Gin
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("templates/*")

	r.GET("/blog/:slug", func(c *gin.Context) {
		slug := c.Param("slug")
		post := getPost(slug)
		c.HTML(http.StatusOK, "post.html", gin.H{
			"Title":        post.Title,
			"Description":  post.Excerpt,
			"OGImage":      "https://yourdomain.com/og/" + slug + ".png",
			"CanonicalURL": "https://yourdomain.com/blog/" + slug,
		})
	})

	r.Run(":8080")
}
{{/* templates/post.html */}}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{.Title}}</title>
  <meta property="og:title"       content="{{.Title}}">
  <meta property="og:description" content="{{.Description}}">
  <meta property="og:image"       content="{{.OGImage}}">
  <meta property="og:url"         content="{{.CanonicalURL}}">
  <meta property="og:type"        content="article">
  <meta name="twitter:card"       content="summary_large_image">
  <meta name="twitter:image"      content="{{.OGImage}}">
  <link rel="canonical" href="{{.CanonicalURL}}">
</head>
<body><h1>{{.Title}}</h1></body>
</html>

Echo and Fiber

Both follow the same pattern — render an HTML template with OG meta values injected:

// Echo
e.GET("/blog/:slug", func(c echo.Context) error {
	slug := c.Param("slug")
	post := getPost(slug)
	return c.Render(http.StatusOK, "post.html", map[string]interface{}{
		"Title":        post.Title,
		"Description":  post.Excerpt,
		"OGImage":      "https://yourdomain.com/og/" + slug + ".png",
		"CanonicalURL": "https://yourdomain.com/blog/" + slug,
	})
})

// Fiber
app.Get("/blog/:slug", func(c *fiber.Ctx) error {
	slug := c.Params("slug")
	post := getPost(slug)
	return c.Render("post", fiber.Map{
		"Title":        post.Title,
		"Description":  post.Excerpt,
		"OGImage":      "https://yourdomain.com/og/" + slug + ".png",
		"CanonicalURL": "https://yourdomain.com/blog/" + slug,
	})
})

Go OG image generation with the image package

// og_image.go — generates a simple OG PNG from a title string
package main

import (
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"net/http"
)

func ogImageHandler(w http.ResponseWriter, r *http.Request) {
	img := image.NewRGBA(image.Rect(0, 0, 1200, 630))
	draw.Draw(img, img.Bounds(), &image.Uniform{color.RGBA{17, 17, 17, 255}}, image.Point{}, draw.Src)

	// Add text using a font package like golang.org/x/image/font
	// (simplified for clarity — use freetype or go-text/typesetting in production)

	w.Header().Set("Content-Type", "image/png")
	w.Header().Set("Cache-Control", "public, max-age=86400")
	png.Encode(w, img)
}

Verify your Go app's OG tags

After deploying, paste your URL into OGFixer to preview how your Go app's links appear on Twitter, LinkedIn, Slack, and Discord — and catch any OG tag issues before they go live.