DEV Community

Cover image for 🌐 Understanding CORS (Cross-Origin Resource Sharing): A Complete Guide
Fazal Mansuri
Fazal Mansuri

Posted on

🌐 Understanding CORS (Cross-Origin Resource Sharing): A Complete Guide

πŸ”Ž What is CORS?

Cross-Origin Resource Sharing (CORS) is a browser security feature that controls how web applications request resources (APIs, images, fonts, etc.) from a different origin (domain, protocol or port).

πŸ‘‰ Example:

  • Your React frontend runs at http://localhost:3000.
  • Your Go API runs at http://localhost:8080.
  • When React fetches data from Go API β†’ this is a cross-origin request.

Without proper CORS setup, the browser blocks the request for security reasons.


βš™οΈ How Does CORS Work?

CORS works via HTTP headers exchanged between the browser and server.

1. Simple Requests

Happen when:

  • Method is GET, POST or HEAD
  • Headers are only "safe" ones (Accept, Content-Type as application/x-www-form-urlencoded, multipart/form-data, text/plain)

πŸ‘‰ Example:

GET /api/products HTTP/1.1
Origin: http://example-frontend.com
Enter fullscreen mode Exit fullscreen mode

Server Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example-frontend.com
Enter fullscreen mode Exit fullscreen mode

If the header matches β†’ browser allows. Otherwise, request is blocked.


2. Preflight Requests

For non-simple requests (e.g., PUT, DELETE or custom headers), the browser sends a preflight request using OPTIONS.

What is Preflight Request?
Not all requests go directly to the server. Sometimes, the browser first asks:
"Hey server, is it safe if I send this request?" - This is called a preflight request.
A preflight request is an OPTIONS call that the browser automatically makes before the actual request, when:

  • The HTTP method is not simple (GET, POST, HEAD).
  • The request has custom headers (e.g., Authorization, X-Custom-Header).
  • The Content-Type is not "safe" (application/x-www-form-urlencoded, multipart/form-data, text/plain).

πŸ‘‰ Example:

OPTIONS /api/products HTTP/1.1
Origin: http://example-frontend.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
Enter fullscreen mode Exit fullscreen mode

Server must respond with allowed methods/headers:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://example-frontend.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Enter fullscreen mode Exit fullscreen mode

Then, the actual request (PUT/DELETE) is sent.


βœ… Why is CORS Important?

  • Protects Users: Prevents malicious sites from sending requests to other sites using your credentials (Cross-Site Request Forgery protection).
  • Granular Control: You decide which origins, methods and headers are safe.
  • Modern Requirement: Any modern web app with frontend + backend on different domains needs correct CORS setup.

⚠️ Common Issues Caused by CORS

1️⃣ Blocked by CORS Policy (No 'Access-Control-Allow-Origin' header)

  • Server not configured to allow the frontend domain.

2️⃣ Credential Errors

  • Cookies or tokens require:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://myapp.com
Enter fullscreen mode Exit fullscreen mode

❌ Note: * cannot be used when credentials are allowed.

3️⃣ Preflight Failures

  • Backend doesn’t respond correctly to OPTIONS.

4️⃣ Wildcard Misuse

  • Using Access-Control-Allow-Origin: * everywhere β†’ insecure.

πŸ› οΈ Example: CORS in Go (Gin Framework)

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

func main() {
    r := gin.Default()

    r.Use(cors.New(cors.Config{
        AllowOrigins:     []string{"http://localhost:3000", "https://myapp.com"},
        AllowMethods:     []string{"GET", "POST", "PUT", "DELETE"},
        AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
        ExposeHeaders:    []string{"Content-Length"},
        AllowCredentials: true,
        MaxAge: 12 * time.Hour,
    }))

    r.GET("/api/products", func(c *gin.Context) {
        c.JSON(200, gin.H{"product": "Laptop"})
    })

    r.Run(":8080")
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ This setup allows React (localhost:3000) or production frontend to call your Go API.


🌍 Real-World Examples

  • Frontend hosted on Netlify (https://myapp.netlify.app) calling backend on AWS EC2 (https://api.myapp.com).
  • Without CORS β†’ browser blocks requests.
  • With proper CORS headers β†’ smooth API calls.

πŸ”’ Best Practices

  • βœ… Always restrict origins β†’ never * for sensitive APIs.
  • βœ… For multiple environments (dev/staging/prod), configure env-specific origins.
  • βœ… Handle preflight (OPTIONS) requests in backend properly.
  • βœ… Use Access-Control-Max-Age to reduce repeated preflights.
  • βœ… Avoid allowing unnecessary headers/methods.
  • βœ… Never rely only on CORS for security β†’ also use authentication & rate limiting.

πŸ“Œ Latest Info & Trends

  • CORS in Fetch API:
    fetch(url, { mode: "cors", credentials: "include" }) must match backend CORS setup.

  • Private Network Access (PNA):
    Browsers now add extra preflights for requests to private networks (like http://192.168.x.x).

  • Proxies in Dev:

    • React (create-react-app) uses proxy in package.json to bypass CORS in local dev.
    • In production β†’ must fix at backend.

🎯 Final Thoughts

CORS might look like a β€œweird error message” at first, but it’s one of the most important browser security features.

If your frontend talks to your backend (different domain/port), you must configure CORS properly β€” otherwise, users face blocked requests.

πŸ‘‰ The key takeaway:

  • Don’t just throw * in Access-Control-Allow-Origin.
  • Think carefully about which origins, headers and methods are safe.
  • Set it once, document it and avoid long debugging sessions.

Top comments (0)