Backend Development 27 min read

Understanding and Implementing CORS (Cross‑Origin Resource Sharing) in Go

This article explains the browser same‑origin policy, defines cross‑origin requests, details the CORS mechanism—including simple and non‑simple requests and their required HTTP headers—and provides practical Go examples using Gin middleware and Kubernetes to solve CORS issues.

Go Programming World
Go Programming World
Go Programming World
Understanding and Implementing CORS (Cross‑Origin Resource Sharing) in Go

What Is Cross‑Origin

The browser’s same‑origin policy, introduced by Netscape in 1995, restricts interactions between pages that differ in protocol, host, or port; such pages are considered "different origin" and cannot share cookies, DOM, or AJAX responses.

Cross‑Origin Resource Sharing (CORS)

CORS is a W3C standard that lets a server explicitly allow cross‑origin requests by adding specific HTTP response headers. The browser sends an Origin header, and the server replies with Access-Control-Allow-Origin and other CORS headers.

Simple Requests

A request is simple when it uses GET, HEAD, or POST, only safe request headers (Accept, Accept‑Language, Content‑Language, Content‑Type with limited MIME types, Range), and no ReadableStream . The browser automatically adds the Origin header.

GET /resources/public-data/ HTTP/1.1
Host: foo.example
Origin: https://foo.example
...

The server’s response must contain Access-Control-Allow-Origin (e.g., * or a specific origin) to permit the response.

Non‑Simple Requests

For methods other than GET/HEAD/POST, custom headers, or non‑safe MIME types, the browser first sends a preflight OPTIONS request to verify permissions.

OPTIONS /doc HTTP/1.1
Host: foo.example
Origin: https://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
...

If the server replies with appropriate Access-Control-Allow-* headers, the browser proceeds with the actual request.

CORS Headers Overview

Access-Control-Allow-Origin – required, specifies allowed origin(s).

Access-Control-Allow-Methods – allowed HTTP methods for preflight.

Access-Control-Allow-Headers – allowed request headers for preflight.

Access-Control-Expose-Headers – response headers exposed to JavaScript.

Access-Control-Allow-Credentials – whether cookies/auth info may be sent.

Access-Control-Max-Age – how long preflight results may be cached.

Timing-Allow-Origin and Vary – optional for performance data and caching.

Implementing CORS in Go

Below are minimal examples using the Gin framework.

Simple Request Example

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/data", func(c *gin.Context) {
        c.Header("Access-Control-Allow-Origin", "*")
        c.JSON(200, gin.H{"message": "这是配置了 CORS 的响应"})
    })
    r.Run(":8000")
}

Running this server and accessing it from a page served on a different port returns the expected JSON without CORS errors.

Non‑Simple Request Middleware

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.Use(Cors)
    r.POST("/data", func(c *gin.Context) {
        var requestData map[string]interface{}
        if err := c.BindJSON(&requestData); err != nil {
            c.JSON(400, gin.H{"error": "Invalid JSON"})
            return
        }
        c.JSON(200, gin.H{"message": "非简单请求已成功", "requestData": requestData})
    })
    r.Run(":8000")
}

func Cors(c *gin.Context) {
    if c.Request.Header.Get("Origin") == "" { c.Next(); return }
    c.Header("Access-Control-Allow-Origin", "*")
    if c.Request.Method == "OPTIONS" {
        c.Header("Access-Control-Allow-Headers", "Content-Type,X-Custom-Header")
        c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
        c.Header("Content-Type", "application/json")
        c.AbortWithStatus(204)
        return
    }
    c.Next()
}

The middleware adds the necessary CORS headers and handles the preflight request.

Using Gin’s Official CORS Middleware

package main

import (
    "strings"
    "time"
    "github.com/gin-contrib/cors"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.Use(cors.New(cors.Config{
        AllowMethods: []string{"GET", "POST", "OPTIONS"},
        AllowHeaders: []string{"Content-Type", "X-Custom-Header"},
        ExposeHeaders: []string{"X-jwt-token"},
        AllowCredentials: true,
        AllowOriginFunc: func(origin string) bool {
            return strings.HasPrefix(origin, "http://localhost") || strings.HasSuffix(origin, "jianghushinian.cn")
        },
        MaxAge: 1 * time.Hour,
    }))
    r.POST("/data", func(c *gin.Context) {
        var requestData map[string]interface{}
        if err := c.BindJSON(&requestData); err != nil {
            c.JSON(400, gin.H{"error": "Invalid JSON"})
            return
        }
        c.Header("X-jwt-token", "fake-token")
        c.JSON(200, gin.H{"message": "非简单请求已成功", "requestData": requestData})
    })
    r.Run(":8000")
}

This middleware automatically sets all required CORS headers and supports origin‑matching functions.

CORS in Kubernetes

Kubernetes’ API server provides a WithCORS filter that wraps an http.Handler and injects the same set of headers. It accepts regular‑expression origin patterns, allowing flexible matching similar to Gin’s AllowOriginFunc .

func WithCORS(handler http.Handler, allowedOriginPatterns []string, allowedMethods []string, allowedHeaders []string, exposedHeaders []string, allowCredentials string) http.Handler {
    // ... sets Access-Control-Allow-Origin, -Methods, -Headers, -Expose-Headers, -Credentials
    // Handles preflight OPTIONS by returning 204 No Content
    // Delegates to the wrapped handler for actual requests
}

The implementation demonstrates how to configure CORS without a dedicated library, reinforcing the underlying principles.

Conclusion

By understanding the same‑origin policy and the CORS handshake (simple vs. non‑simple), developers can correctly configure server responses in Go, Gin, or Kubernetes to eliminate browser‑side cross‑origin errors, while remembering that non‑browser tools like curl or Postman are unaffected.

backendGoWeb DevelopmentCORSCross-OriginGin
Go Programming World
Written by

Go Programming World

Mobile version of tech blog https://jianghushinian.cn/, covering Golang, Docker, Kubernetes and beyond.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.