gin-contrib/cors: CORS middleware for Gin with a 14-field configuration struct
Official CORS gin's middleware
At a glance
- What is it?
- gin-contrib/cors is the official CORS middleware for Gin, configured through a cors.Config struct. cors.Default() allows all origins but disables cookies for clients; AllowAllOrigins and AllowCredentials cannot both be true.
- Who is it for?
- gin-contrib/cors fits any Gin API that needs CORS headers without writing them by hand in each handler. Skip it if the application already has a hand-rolled CORS middleware and the added dependency is not worth the trade.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
CORS preflight handling in Gin and why a dedicated middleware helps
A browser making a cross-origin request first sends an HTTP OPTIONS preflight to check whether the server permits it. Gin handles routing but provides no built-in logic to inspect those preflight requests, add the required Access-Control response headers, or decide which origins are allowed. Every Gin API that accepts cross-origin requests has to handle this explicitly.
gin-contrib/cors solves this with a middleware that intercepts requests, reads the Origin header, compares it against the configured policy, and writes the Access-Control response headers. For preflight OPTIONS requests it returns early with the headers and does not call the real handler. For regular cross-origin requests it adds the appropriate headers to the outgoing response.
Without a middleware, the alternative is writing those headers directly in each handler or in a hand-rolled middleware. gin-contrib/cors extracts that logic into a reusable component with a configuration struct, so the CORS policy is declared once per router. The tradeoff is a dependency: gin v1.12.0 is required, as stated in the go.mod.
Installing gin-contrib/cors and the first working call
Install the package with:
go get github.com/gin-contrib/corsThen import it:
import "github.com/gin-contrib/cors"To allow all origins immediately, attach the middleware to a router:
func main() {
router := gin.Default()
router.Use(cors.Default()) // All origins allowed by default
router.Run()
}`cors.Default()` registers the middleware with all origins permitted. The README warns directly: 'Allowing all origins disables cookies for clients. For credentialed requests, do not allow all origins.' That warning addresses a browser behavior: when a server responds with a wildcard origin, the browser refuses to send cookies to it regardless of what the server intends. An API that never uses cookies is not affected.
The go.mod requires Go 1.26.0. gin-contrib/cors is MIT licensed; v1.7.9 shipped on 2026-09-21, and the last push to master was on 2026-10-03.
cors.Default() and cors.DefaultConfig() take opposite positions on all-origins
The README explains a specific difference between two convenience functions. `Default()` allows all origins. `DefaultConfig()` does not. That distinction matters for code that starts with a default configuration and then overrides individual fields.
Starting from `cors.DefaultConfig()` is the safer production baseline:
config := cors.DefaultConfig()
config.AllowOrigins = []string{"http://google.com"}
router.Use(cors.New(config))`DefaultConfig()` returns a config with `AllowAllOrigins` set to false. Setting `AllowOrigins` to a list then adds specific origins without enabling all-origins behavior. Calling `Default()` is equivalent to passing `AllowAllOrigins` as true.
To get all-origins behavior from a config struct explicitly, set `AllowAllOrigins = true`. The README note catches a common assumption: starting from defaults does not mean starting with a policy safe for production. `DefaultConfig()` has no origin allowed until the caller adds one, so an application that switches from `Default()` to `DefaultConfig()` without adding `AllowOrigins` would reject all cross-origin requests.
`AllowMethods` defaults to a list of 7 HTTP methods: GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS. Custom request headers and custom response headers each start with empty default lists.
AllowCredentials requires a specific origin, not AllowAllOrigins
Setting `AllowCredentials` to true tells the browser to include cookies, HTTP authentication, and client certificates in cross-origin requests. For this to work, the server must respond with a specific origin value in the Access-Control-Allow-Origin header rather than a wildcard. Enabling all-origins produces a wildcard response and breaks that requirement.
The configuration table in the README states this for `AllowAllOrigins`: 'Credentials cannot be used.' Setting both `AllowAllOrigins` and `AllowCredentials` to true produces contradictory behavior. What the README does not document is the exact outcome: whether the middleware rejects the configuration at startup, silently ignores one field, or lets the contradictory headers reach the client. A developer who hits this combination should not assume the middleware will catch it.
For an API that needs both flexible origin matching and credentialed requests, `AllowOriginFunc` is the correct path. A function that returns true for an origin sends back that specific origin string, not a wildcard, so cookies work. Setting `AllowAllOrigins` to true produces a wildcard response that breaks that requirement; the restriction applies to the response header, not to the server-side decision logic.
AllowOriginFunc takes precedence over AllowOrigins and makes the list irrelevant
Two config fields support dynamic origin validation. `AllowOriginFunc` takes a function with the signature `func(string) bool` that receives the origin and returns true if allowed. `AllowOriginWithContextFunc` takes `func(*gin.Context,string)bool`, adding access to the full Gin request context for decisions that depend on request data beyond the origin header.
The README's configuration table includes a one-line note for `AllowOriginFunc`: 'If set, AllowOrigins is ignored.' That note has a consequence that is easy to miss: a developer who sets both `AllowOriginFunc` and `AllowOrigins`, perhaps to combine a static allowlist with a dynamic check, gets only the function. Setting `AllowOrigins` alongside `AllowOriginFunc` has no effect on which origins are permitted. The function is the entire origin check.
An example from the README:
AllowOriginFunc: func(origin string) bool {
return origin == "https://github.com"
},`AllowOriginWithContextFunc` is listed in the config table without a code example in the README. Its use case is requests where the decision depends on session data, query parameters, or other per-request information.
MaxAge controls how long preflight responses stay cached in the browser
MaxAge sets the number of seconds a browser caches the response to an OPTIONS preflight request. Its default value is `12 * time.Hour`. During that window, a browser reuses the cached preflight result and skips the round-trip for subsequent requests to the same endpoint with the same method and headers.
A shorter MaxAge means the browser reissues preflight more often, adding latency to the first request after the cache expires but giving the server more frequent opportunities to change the policy. A longer MaxAge reduces that overhead but delays propagation of any configuration change.
`ExposeHeaders` is a separate field that lists the response headers the browser is permitted to read from cross-origin responses. Without entries there, client JavaScript can only access a limited set of safe headers even when the request succeeds. Any custom response header that client code needs to read must appear in `ExposeHeaders`.
`AllowHeaders` covers the incoming side: non-simple headers included in a request must be explicitly listed. The default is an empty list. An API that accepts a custom Authorization header or a non-standard content type header needs to add those to `AllowHeaders` or preflight will reject them.
Wildcards, Private Network Access, browser extensions and custom URI schemes
Four fields handle origin types outside the standard HTTPS domain pattern. `AllowWildcard` enables patterns like `https://*.example.com` in `AllowOrigins`. Setting `AllowWildcard` to true alone does nothing; the wildcard pattern must also appear in the `AllowOrigins` list.
`AllowPrivateNetwork` adds the Private Network Access CORS header, which browsers require for requests from a public origin to a private network address, following the specification at wicg.github.io/private-network-access/.
`AllowBrowserExtensions` extends origin matching to browser extension schemes such as `chrome-extension://`. `AllowWebSockets` handles WebSocket upgrade request origins. The README's configuration table is truncated before the full description of `AllowWebSockets` appears, so the complete behavior is not documented there.
`CustomSchemas` is a string list for any URI scheme not covered by the other fields. The README gives `tauri://` as an example. All four of these fields default to false or nil, so a standard HTTPS web application needs none of them. They exist for specific environments where the default origin matching rules do not apply.
Editorial conclusion
gin-contrib/cors fits any Gin API that needs CORS headers without writing them by hand in each handler. Skip it if the application already has a hand-rolled CORS middleware and the added dependency is not worth the trade. Before deploying, verify the AllowOrigins list against the actual clients, because a misconfigured AllowAllOrigins combined with AllowCredentials produces contradictory headers that the README does not document as an error. For APIs using custom request headers, check that each one appears in AllowHeaders, since the default is an empty list.
Frequently asked questions
What does gin-contrib/cors do?
gin-contrib/cors is a CORS middleware for the Gin web framework. It intercepts cross-origin requests, checks the origin against a configured policy, and writes the appropriate Access-Control response headers, handling OPTIONS preflight requests without calling the actual handler.
How do I install gin-contrib/cors?
Run go get github.com/gin-contrib/cors to fetch the package, then import it with import "github.com/gin-contrib/cors". Attach the middleware to a Gin router with router.Use(cors.Default()) for all-origins, or router.Use(cors.New(config)) for a custom cors.Config.
Can gin-contrib/cors handle credentialed requests with cookies?
Yes, by setting AllowCredentials: true in cors.Config, but only when AllowAllOrigins is false and specific origins are listed or AllowOriginFunc is set. AllowAllOrigins and AllowCredentials cannot both be true; the README states that credentials cannot be used when all origins are allowed.
What is the difference between cors.Default() and cors.New() in gin-contrib/cors?
cors.Default() registers the middleware with all origins allowed, equivalent to AllowAllOrigins: true. cors.New() takes an explicit cors.Config struct, giving full control over origins, methods, headers, credentials and caching. cors.DefaultConfig() returns a default-populated struct without enabling all-origins.
How does AllowOriginFunc work in gin-contrib/cors?
AllowOriginFunc takes a function with the signature func(string) bool that receives the request origin and returns true if it is allowed. When AllowOriginFunc is set, the AllowOrigins list is ignored entirely. AllowOriginWithContextFunc is a variant that also receives the Gin context.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/gin-contrib-cors)Was this useful?