Why do we get CORS error and how to resolve it?

If you have been developing REST APIs that are consumed by front end applications, it’s likely that you’ve come across this error, which is annoying for both frontend and backend developers. So let’s start with why we get this error? It’s because browsers don’t allow application from a origin to access resources on a different origin. This means that if your client application has a different domain or port than your backend, bowser will restrict the access to the resource on the backend. 

Same-origin policy (SOP)

The Same-origin policy is developed as a security mechanism for browsers which allows resource sharing on same origin. For two websites to have the same origins, the websites should have the same domain, port number, and protocol type. If any one of the three properties is found differently, then the sources are considered different origins.

Cross Origin Resource Sharing (CORS)

Lets see, how we can let the browser know that the front end application is allowed to access the resources on our backend. CORS is a protocol that makes it possible. Cross Origin Resource Sharing (CORS) mechanism uses HTTP-headers to indicate that resource can be shared between cross origin. Browser makes a preflight (OPTIONS) request to the server to make sure that it will permit the actual request. 

Preflight Request

Preflight Request Headers

A preflight request contains headers that indicates the origin, method type and headers the actual request will contain. 

OPTIONS /doc HTTP/1.1
Host: abc.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type
  • Origin: The Origin request header indicates the origin (scheme, hostname, and port) that caused the request.
  • Access-Control-Request-Method: This header is used by the client to let the server know which HTTP method will be used when the actual request is made.
  • Access-Control-Request-Headers: This header is used by the client to let the server know which HTTP headers the client might send when the actual request is made.

Preflight Response Headers

Once the server receives this request it is the responsibility of server to determine whether or not to permit request. If the server permits the request from this origin it must add following headers to the response of preflight response.

  • Access-Control-Allow-Origin — The whitelisted origin, or ‘*’
  • Access-Control-Allow-Methods — A comma-separated list of HTTP methods the web server wishes to permit for cross-origin requests
  • Access-Control-Allow-Headers — A comma-separated list of HTTP headers the web server wishes to permit for cross-origin requests

There are other headers that can be added:

  • Access-Control-Allow-Credentials—Server allows cookies (or other user credentials) to be included on cross-origin requests.
  • Access-Control-Max-Age— Indicates how long the result of preflight request can be cached.
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization,Content-Type
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
Access-Control-Allow-Origin: https://foo.example
Access-Control-Max-Age: 3600
Content-Length: 0

Response in Actual Request

With these headers browser can determine if server permits actual request. Server must also add Access-Control-Allow-Origin to the response of actual request.

When credentials are passed with the request to the cross-origin server, the browser will not allow access to the response unless the cross-origin server sends a CORS header Access-Control-Allow-Credentials with a value of true.

Sample filter that add CORS header

public class CorsFilter extends OncePerRequestFilter { 

@Value("${hosts}"
private Set<String> allowDomains;

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,FilterChain    filterChain) throws ServletException, IOException { 
String originHeader = request.getHeader(HttpHeaders.ORIGIN);
    if (allowDomains.contains(originHeader)) {
      response.setHeader("Access-Control-Allow-Origin",      originHeader);
      response.setHeader("Access-Control-Allow-Credentials", "true");
      if(HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) {
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Authorization,Content-Type");
        response.setStatus(HttpServletResponse.SC_OK);
      } else {
        filterChain.doFilter(request, response);
      }
    } else {
      filterChain.doFilter(request, response);
    }
  }

}

Refrences:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://reflectoring.io/complete-guide-to-cors/

Don’t miss these tips!

We don’t spam! Read our privacy policy for more info.

Sharing is caring!

Leave a Comment

Your email address will not be published.