If you search Cors error, you can find tons of answers. But most of them are not clear enough to understand, especially you are new to web development. So I will explain what is Cors error and how to fix it in Golang.
1. What is Cors?
CORS stands for Cross-Origin Resource Sharing. It is a security feature implemented by web browsers to control how web pages in one domain can request and interact with resources hosted on another domain.
Okay, I knew it’s same explanation as you can find on the internet. But I will explain it in a different way from now on.
What does “Origin” mean in the context of web development and security?
An origin refers to the combination of the following three components:
- Scheme: This refers to the protocol used, such as HTTP or HTTPS.
- Host: This is the domain name (e.g., www.example.com).
- Port: This is the port number on the server where the resource is hosted. It is optional and often implied based on the protocol (e.g., port 80 for HTTP, port 443 for HTTPS).
Together, these three components make up the origin of a web page or web application. So you can think of an origin as a web page’s address (aka URL), but it is only a fundamental security concept in web browsers.
So now we explained what is “Origin”, we can explain what is “Cross-Origin”.
What is the Cross-Origin?
Combined with the above explanation, we can think of the Cross-Origin as a web page’s address which is different from the current web page’s address. Okay. It’s still not clear. Let’s see an example.
Let’s say you have a website www.frontend.com
and you want to request data from www.api.com
. So this is a Cross-Origin request because the origin of www.frontend.com
is different from the origin of www.api.com
.
Why I don’t have such a problem when I use Postman?
Because Postman is not a web browser. It is a tool for API development. So it doesn’t have the same security feature as a web browser.
2. How to fix it in Golang
Why I specify the solution in Golang?
First, I develop backend project in Golang. It’s a personal preference. But the solution is similar in other languages.
Second, the Cors error only needs to be fixed in the backend (server-side).
Let’s get started with an example.
2.1. Create a simple Golang server
1// main.go
2package main
3
4import (
5 "encoding/json"
6 "fmt"
7 "log"
8 "net/http"
9)
10
11type Cat struct {
12 Name string
13 Age int
14 Breed string
15 Color string
16 Weight float64
17}
18
19func init() {
20 dummyCats = make([]Cat, 5)
21 dummyCats[0] = Cat{Name: "Whiskers", Age: 3, Breed: "Siamese", Color: "Brown", Weight: 8.5}
22 dummyCats[1] = Cat{Name: "Mittens", Age: 2, Breed: "Tabby", Color: "Gray", Weight: 7.2}
23 dummyCats[2] = Cat{Name: "Fluffy", Age: 4, Breed: "Persian", Color: "White", Weight: 10.0}
24 dummyCats[3] = Cat{Name: "Tiger", Age: 5, Breed: "Bengal", Color: "Spotted", Weight: 12.3}
25 dummyCats[4] = Cat{Name: "Smokey", Age: 1, Breed: "Russian Blue", Color: "Blue", Weight: 6.8}
26
27}
28
29var dummyCats []Cat
30
31func main() {
32 http.HandleFunc("/cats", listCatsHandler)
33 log.Fatal(http.ListenAndServe(":8082", nil))
34}
35
36func listCatsHandler(w http.ResponseWriter, r *http.Request) {
37 if r.Method != http.MethodGet {
38 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
39 return
40 }
41
42 resp, err := json.Marshal(dummyCats)
43 if err != nil {
44 http.Error(w, err.Error(), http.StatusInternalServerError)
45 return
46 }
47
48 w.Header().Set("Content-Type", "application/json")
49 fmt.Fprintf(w, "%s", resp)
50}
In this example, I create a simple Golang server which returns a list of cats in JSON format. It will be hosted on http://localhost:8082/cats
. Hit go run main.go
and test it with Thunder Client (Postman-like plugin).
It works fine. 🎉
Okay, now let’s try to call this URL on web browser.
It still works?
Don’t get confused. It works fine because you are requesting data from web browser to the Golang server. It is not a Cross-Origin request.
Remeber, the Cross-Origin request is a request from a different origin (URL). Typically, you request data from web browser to your frontend page (www.fronend.com/page
). Then your frontend page requests data from your backend server(www.backend.com/api
). So it is a Cross-Origin request.
2.2 Create a simple frontend page
Let’s create a simple frontend page hosted by Golang. It will be hosted on http://localhost:8080/
.
The reason I create a frontend page hosted by Golang is that I want to make the example more clear. You can input http://localhost:8080/
in your web browser to see the frontend page. Then you can open the developer tool and see the origin of the frontend page is different from the origin of the backend server.
Alternatively, you can just create a html file and write some javascript code to request data from your backend server. And then open the html file in your web browser. It will also show the Cross-Origin error. But it is not clear enough to understand.
1// main.go
2package main
3
4import (
5 "log"
6 "net/http"
7)
8
9func main() {
10 // Serve static files from the "static" directory
11 http.Handle("/", http.FileServer(http.Dir("static")))
12 log.Fatal(http.ListenAndServe(":8080", nil))
13}
After creating the main.go
file. Create a static
folder and put an index.html
file in it. Just keep it lean and simple.
1<!-- index.html -->
2<!DOCTYPE html>
3<html lang="en">
4 <head>
5 <meta charset="UTF-8" />
6 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 <title>Simple Frontend Server - List Cats</title>
8 </head>
9 <body>
10 <h1>Listing Cats</h1>
11
12 <ul id="cats"></ul>
13
14 <script>
15 var apiUrl = "http://localhost:8082/cats";
16
17 // Perform the API request using the Fetch API
18 fetch(apiUrl)
19 .then((response) => {
20 if (!response.ok) {
21 throw new Error("Failed to fetch data from API");
22 }
23 return response.json();
24 })
25 .then((data) => {
26 var ul = document.getElementById("cats");
27 data.forEach((cat) => {
28 var li = document.createElement("li");
29 li.innerHTML = `<span>${cat.Name}, ${cat.Age}, ${cat.Color}, ${cat.Breed}, ${cat.Weight}</span>`;
30 ul.appendChild(li);
31 });
32 })
33 .catch((error) => {
34 console.error("API request failed:", error.message);
35 });
36 </script>
37 </body>
38</html>
💡Line 13: Note that this is the url of the backend server.
Now, you can open http://localhost:8080/
in your web browser and open the developer tool. You will see the Cross-Origin error.
Okay, now we have a clear example. Let’s fix it.
2.3 Fix the Cross-Origin error
The Cross-Origin error is caused by the security feature of the web browser. So we need to fix it in the backend server.
Let’s add the following code in the main
function of the backend server.
1...
2func main() {
3 http.HandleFunc("/cats", listCatsHandler)
4 log.Fatal(http.ListenAndServe(":8082", nil))
5}
6
7func listCatsHandler(w http.ResponseWriter, r *http.Request) {
8 w.Header().Set("Access-Control-Allow-Origin", "http://localhost:8080") // Allow CORS from frontend origin
9 w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
10 w.Header().Set("Access-Control-Allow-Headers", "*")
11 w.Header().Set("Access-Control-Allow-Credentials", "true")
12
13 // Handle preflight requests
14 if r.Method == "OPTIONS" {
15 w.WriteHeader(http.StatusOK)
16 return
17 }
18
19 if r.Method != http.MethodGet {
20 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
21 return
22 }
23
24 adJson, err := json.Marshal(dummyCats)
25 if err != nil {
26 http.Error(w, err.Error(), http.StatusInternalServerError)
27 return
28 }
29
30 w.Header().Set("Content-Type", "application/json")
31 fmt.Fprintf(w, "%s", adJson)
32}
I will explain the code line by line. But let’s see the result first.
The Cross-Origin error is gone. 🎉
💡Line 8-11: These lines are used to allow CORS from the frontend origin. You can specify the frontend origin in the Access-Control-Allow-Origin
header. In this example, I specify the frontend origin as http://localhost:8080
. You can also use *
to allow all origins. But it is not recommended in production.
💡Line 13-16: These lines are used to allow CORS methods. In this example, I only allow GET
and OPTIONS
methods. You can also use *
to allow all methods. But it is not recommended in production.
In this example, we add the Cors error fix inside of the listCatsHandler
function. But if you have more than one endpoint, it is better to add a Cors middleware to handle the Cors error. For example,
1func corsMiddleware(next http.Handler) http.Handler {
2 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3 w.Header().Set("Access-Control-Allow-Origin", "*")
4 w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
5 w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Origin, Authorization")
6 w.Header().Set("Access-Control-Allow-Credentials", "true")
7
8 if r.Method == http.MethodOptions {
9 w.WriteHeader(http.StatusOK)
10 return
11 }
12
13 next.ServeHTTP(w, r)
14 })
15}
I will not change my example with the Cors middleware, as it is not the main point of this article. Just a reminder, the above middleware code does not fit the handler function in the example. You need to modify it to make it work.
3. Last words
Just in case that if you have a question about the “Cross-Origin” request from a backend server(www.backend1.com
) to another backend server(www.backend2.com
).
If API one sends a request to API two, and the user only interacts with API one through a web browser, the scenario doesn’t involve a traditional Cross-Origin Resource Sharing (CORS) issue from the perspective of the browser. CORS restrictions are enforced by web browsers to prevent web pages from making requests to domains other than the one that served the web page.
CORS primarily impacts requests made from the client side, where JavaScript running in the browser attempts to make HTTP requests to a different domain. When server-to-server communication occurs (such as API one communicating with API two on the server side), the same-origin policy and CORS restrictions are not applicable, as the server-side code doesn’t operate within the constraints imposed by the browser.
Therefore, if the user interacts with API one through a web browser, and API one makes requests to API two on the server side, you typically won’t encounter CORS-related issues in this specific communication between the APIs. However, other security considerations and authentication mechanisms between the APIs may still be relevant.
4. Reference
You can find the code from my GitHub repo here and ⭐ if you like it.
If this post helped you to solve a problem or provided you with new insights, please upvote it and share your experience in the comments below. Your comments can help others who may be facing similar challenges. Thank you!