How to enable OPTIONS requests globally?

I am using HTTP handlers in 1.22, however, OPTIONS requests are automatically being rejected and a 405 sent to the client. For this reason, I am unable to call any POST endpoints from my SPA application. Is there a way to automatically allow OPTIONS requests globally without using third party tools/libraries?

I do it this way:

	switch r.Method {
	case "GET":
		Read(w, r)
	case "DELETE":
		Delete(w, r)
	case "POST":
		Create(w, r)
	case "PUT":
		Update(w, r)
	case "OPTIONS":
		// bugfix (handshake)
	default:
		Read(w, r)
	}

You should be able to just use something like this:

func newAppMux() *http.ServeMux {
	router := http.NewServeMux()
	// Global options handler
	router.HandleFunc("OPTIONS /", handleOptions)
	return router
}

func handleOptions(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "https://myapp.com")
	w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
	w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization")
	w.WriteHeader(http.StatusOK)
}

A note about β€œ/” in the pattern. From the docs:

The special wildcard {$} matches only the end of the URL. For example, the pattern β€œ/{$}” matches only the path β€œ/”, whereas the pattern β€œ/” matches every path.

So that matches every path for that method (β€œOPTIONS”). For further reading on CORS and understanding of what headers you might want to set, check this out.

1 Like

There is no need to declare a global OPTIONS handler for CORS, though; and rather than implementing CORS by hand, which is error-prone, I’d recommend using a dedicated library. Here is an example:

package main

import (
  "io"
  "log"
  "net/http"

  "github.com/jub0bs/cors"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("GET /hello", handleHello) // note: not configured for CORS

  // create CORS middleware
  corsMw, err := cors.NewMiddleware(cors.Config{
    Origins:        []string{"https://example.com"},
    Methods:        []string{http.MethodGet, http.MethodPost},
    RequestHeaders: []string{"Authorization"},
  })
  if err != nil {
    log.Fatal(err)
  }
  corsMw.SetDebug(true) // turn debug mode on (optional)

  api := http.NewServeMux()
  api.HandleFunc("GET /users", handleUsersGet)
  api.HandleFunc("POST /users", handleUsersPost)
  mux.Handle("/api/", http.StripPrefix("/api", corsMw.Wrap(api))) // note: method-less pattern here

  log.Fatal(http.ListenAndServe(":8080", mux))
}

func handleHello(w http.ResponseWriter, _ *http.Request) {
  io.WriteString(w, "Hello, World!")
}

func handleUsersGet(w http.ResponseWriter, _ *http.Request) {
  // omitted
}

func handleUsersPost(w http.ResponseWriter, _ *http.Request) {
  // omitted
}