49

I have fairly simple setup here as described in the code below. But I am not able to get the CORS to work. I keep getting this error:

XMLHttpRequest cannot load http://localhost:3000/signup. Response to preflight request doesn't pass access control check: No 'Access- Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 403.

I am sure I am missing something simple here.

Here is the code I have:

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "myApp/src/controllers"
)

func main() {
    ac := new(controllers.AccountController)

    router := mux.NewRouter()
    router.HandleFunc("/signup", ac.SignUp).Methods("POST")
    router.HandleFunc("/signin", ac.SignIn).Methods("POST")

    log.Fatal(http.ListenAndServe(":3000", handlers.CORS()(router)))
}
Moon
  • 33,439
  • 20
  • 81
  • 132

8 Answers8

65

Please read the link Markus suggested, and also about what triggers CORS pre-flight requests.

Pre-flight requests: You may have a content type like JSON, or some other custom header that's triggering a pre-flight request, which your server may not be handling. Try adding this one, if you're using the ever-common AJAX in your front-end: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Requested-With

Gorilla's handlers.CORS() will set sane defaults to get the basics of CORS working for you; however, you can (and maybe should) take control in a more functional manner.

Here's some starter code:

// Where ORIGIN_ALLOWED is like `scheme://dns[:port]`, or `*` (insecure)
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With"})
originsOk := handlers.AllowedOrigins([]string{os.Getenv("ORIGIN_ALLOWED")})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})

// start server listen
// with error handling
log.Fatal(http.ListenAndServe(":" + os.Getenv("PORT"), handlers.CORS(originsOk, headersOk, methodsOk)(router)))
jeremiah.trein
  • 776
  • 6
  • 6
  • 7
    Just some bookkeeping: Safari likes to send along the `Content-Type` request header when performing a "non-standard" operation (e.g. `PUT`), so you'll want to make your CORS headers allow `Content-Type` as well. `headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})` [related reference in Gorilla CORS package](https://github.com/gorilla/handlers/blob/master/cors.go#L170) – jeremiah.trein Dec 23 '16 at 23:55
36

You can get more details here: Why doesn’t Postman get a "No 'Access-Control-Allow-Origin' header is present on the requested resource" error when my JavaScript code does? about this issue.

Also try this handler: Go Cors Handler which should solve your issue. I find this much cleaner and easy to resolve the issue.

package main

import (
    "log"
    "net/http"
    "github.com/rs/cors"
    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "myApp/src/controllers"
)

func main() {
    ac := new(controllers.AccountController)

    router := mux.NewRouter()
    router.HandleFunc("/signup", ac.SignUp).Methods("POST")
    router.HandleFunc("/signin", ac.SignIn).Methods("POST")

    c := cors.New(cors.Options{
        AllowedOrigins: []string{"http://localhost:8000"},
        AllowCredentials: true,
    })

    handler := c.Handler(router)
    log.Fatal(http.ListenAndServe(":3000", handler)
}
Lamanus
  • 12,898
  • 4
  • 21
  • 47
21

You should create a CORSOption object. For example to allow any origin, Use this code:

corsObj:=handlers.AllowedOrigins([]string{"*"})

Then you pass this object to your handle.CORS function:

log.Fatal(http.ListenAndServe(":3000", handlers.CORS(corsObj)(router)))

For testing it you can use CURL:

curl -H "Origin: http://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: X-Requested-With" \
-X OPTIONS --verbose http://127.0.0.1:3000

When it works you should see those headers:

> Accept: */*
> Origin: http://example.com
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: X-Requested-With

Final code is here: https://play.golang.org/p/AOrlJsWhvf

More info:

Community
  • 1
  • 1
Aminadav Glickshtein
  • 23,232
  • 12
  • 77
  • 117
7

I realize this is an old issue but nonetheless it took me 30min to get this right.

handler = handlers.CORS(
    // handlers.AllowedMethods([]string{"GET", "POST", "PUT"}),
    handlers.AllowedHeaders([]string{"Accept", "Accept-Language", "Content-Type", "Content-Language", "Origin"}),
    // handlers.AllowedOrigins([]string{"*"}),
)(handler)

Things to note:

  • AllowedMethods does NOT need to explicitly include OPTIONS, this is part of the CORS handler
  • AllowedHeaders need to be explicitly mentioned, * is not a valid wildcard. Typical ajax libraries will send Content-Type when requesting something like application/json, so add that as well.
  • * is the default for AllowedOrigin
andig
  • 13,378
  • 13
  • 61
  • 98
  • 1
    More details about gorilla/handlers's lack of support for the wildcard in the `Access-Control-Allow-Headers` header in https://stackoverflow.com/questions/70028294/cors-preflight-mysteriously-failing-with-gorilla-handlers/70034623#70034623 – jub0bs Nov 19 '21 at 13:50
6

After declaring the mux object, add the accessControlMiddleware as a middleware to the declared object.

func main(){
  ac := new(controllers.AccountController)

    router := mux.NewRouter()
    router.Use(accessControlMiddleware)
    router.HandleFunc("/signup", ac.SignUp).Methods("POST")
    router.HandleFunc("/signin", ac.SignIn).Methods("POST")
    http.ListenAndServe(":3000", corsOpts.Handler(router))
}

// access control and  CORS middleware
func accessControlMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            w.Header().Set("Access-Control-Allow-Origin", "*")
            w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS,PUT")
            w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")

                if r.Method == "OPTIONS" {
                    return
                }

                next.ServeHTTP(w, r)
            })
        }
TiGo
  • 652
  • 6
  • 11
  • My issue was with not handling the actual request for "OPTIONS", so sticking a return response in the middleware like you have done makes sense. – Josh Weston Mar 12 '21 at 10:17
4
package main

import (
    "log"
    "net/http"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "myApp/src/controllers"
       "github.com/rs/cors"
)

func main() {

     ac := new(controllers.AccountController)

    router := mux.NewRouter()
    router.HandleFunc("/signup", ac.SignUp).Methods("POST")
    router.HandleFunc("/signin", ac.SignIn).Methods("POST")
//cors optionsGoes Below
corsOpts := cors.New(cors.Options{
    AllowedOrigins: []string{"http://localhost:8100"}, //you service is available and allowed for this base url 
    AllowedMethods: []string{
        http.MethodGet,//http methods for your app
        http.MethodPost,
        http.MethodPut,
        http.MethodPatch,
        http.MethodDelete,
        http.MethodOptions,
        http.MethodHead,
    },

    AllowedHeaders: []string{
        "*",//or you can your header key values which you are using in your application

    },
})

    http.ListenAndServe(":3000", corsOpts.Handler(router))
}
  • gorilla/handlers's `AllowedHeaders` currently interprets `"*"` as a literal value as opposed to a wildcard. See https://stackoverflow.com/questions/70028294/cors-preflight-mysteriously-failing-with-gorilla-handlers/70034623#70034623 – jub0bs Nov 19 '21 at 13:48
4

Base on jeremiah.trein's answer.

CORS filters are set on server side. Request may work with Postman and fail with a browser because Postman doesn't send preflight request whereas a browser does.

Setting the CORS filters will allow you to configure the origins, methods and headers that the backend shall accept.

In addition, if your browser emits POST or PUT requests that contain a json payload (which is quite reasonnable), you'll need to add 'Content-Type' to the allowed headers.

Finally the handlers.CORS()(router) does not only work with the http.ListenAndServe function but also with http.Handle().

The snippet of code might as well look like:

router := mux.NewRouter()

// do all your routes declaration

headersOK := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOK := handlers.AllowedOrigins([]string{"*"})
methodsOK := handlers.AllowedMethods([]string{"GET", "POST", "OPTIONS", "DELETE", "PUT"})

http.Handle("/", handlers.CombinedLoggingHandler(os.Stderr, handlers.CORS(headersOK, originsOK, methodsOK)(router)))

It is worth mentionning that i have successfuly used this snippet of code in a Google Cloud Platform Standard AppEngine (and I believe it would work in a Flex AppEngine as well).

avi.elkharrat
  • 6,100
  • 6
  • 41
  • 47
0

The aformentioned package github.com/rs/cors provides a constructor

AllowAll() *Cors

that

...create a new Cors handler with permissive configuration allowing all origins with all standard methods with any header and credentials.

Yurkol
  • 1,414
  • 1
  • 19
  • 28