go 69 lines · 9 steps

Building a CORS middleware in Gin

A configurable Gin middleware that validates the request Origin and emits the right CORS headers, short-circuiting preflight requests.

Explained by highlit
1package middleware
2 
3import (
4 "net/http"
5 "strconv"
6 "strings"
7 "time"
8 
9 "github.com/gin-gonic/gin"
10)
11 
12type CORSConfig struct {
13 AllowOrigins []string
14 AllowMethods []string
15 AllowHeaders []string
16 AllowCredentials bool
17 MaxAge time.Duration
18}
19 
20func CORS(cfg CORSConfig) gin.HandlerFunc {
21 allowed := make(map[string]struct{}, len(cfg.AllowOrigins))
22 wildcard := false
23 for _, o := range cfg.AllowOrigins {
24 if o == "*" {
25 wildcard = true
26 }
27 allowed[o] = struct{}{}
28 }
29 
30 methods := strings.Join(cfg.AllowMethods, ", ")
31 headers := strings.Join(cfg.AllowHeaders, ", ")
32 maxAge := strconv.Itoa(int(cfg.MaxAge.Seconds()))
33 
34 return func(c *gin.Context) {
35 origin := c.GetHeader("Origin")
36 if origin == "" {
37 c.Next()
38 return
39 }
40 
41 _, ok := allowed[origin]
42 if !ok && !wildcard {
43 c.Next()
44 return
45 }
46 
47 h := c.Writer.Header()
48 if wildcard && !cfg.AllowCredentials {
49 h.Set("Access-Control-Allow-Origin", "*")
50 } else {
51 h.Set("Access-Control-Allow-Origin", origin)
52 h.Add("Vary", "Origin")
53 }
54 
55 if cfg.AllowCredentials {
56 h.Set("Access-Control-Allow-Credentials", "true")
57 }
58 
59 if c.Request.Method == http.MethodOptions {
60 h.Set("Access-Control-Allow-Methods", methods)
61 h.Set("Access-Control-Allow-Headers", headers)
62 h.Set("Access-Control-Max-Age", maxAge)
63 c.AbortWithStatus(http.StatusNoContent)
64 return
65 }
66 
67 c.Next()
68 }
69}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Precomputing lookup structures and joined strings once at setup keeps the per-request handler cheap.
  2. 2CORS requires echoing a specific Origin (plus a Vary header) rather than a wildcard whenever credentials are allowed.
  3. 3Preflight OPTIONS requests should be answered and aborted early with 204 instead of reaching route handlers.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a CORS middleware in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code