Nginx Access-Control-Allow-Origin

Nginx Access-Control-Allow-Origin and CORS

Nginx Access-Control-Allow-Origin header is part of CORS standard (stands for Cross-origin resource sharing) and used to control access to resources located outside of the original domain sending the request.

This standard was created to overcome same-origin security restrictions in browsers, that prevent loading resources from different domains. With the raise of single page apps relying heavily on external API’s and JavaScript apps in general, the need for CORS server configuration is greater than ever. Please note that Fonts ( @font-face within CSS ) and potentially other resources are also affected by same-origin policy.

Ok, so here is the sample of CORS configuration for Nginx:

server {
  listen        80;
  server_name   api.test.com;


  location / {

    # Simple requests
    if ($request_method ~* "(GET|POST)") {
      add_header "Access-Control-Allow-Origin"  *;
    }

    # Preflighted requests
    if ($request_method = OPTIONS ) {
      add_header "Access-Control-Allow-Origin"  *;
      add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD";
      add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept";
      return 200;
    }

    ....
    # Handle request
    ....
  }
}

As you can tell by Access-Control-Allow-Origin * – this is wide open configuration, meaning any client will be able to access the resource.

You can list specific hostnames that are allowed to access the server:

add_header "Access-Control-Allow-Origin" "http://test.com, https://example.com"

If you wonder what’s if ($request_method = OPTIONS ) condition, you are not alone. There is slightly confusing concept of Simple and Pre-flight CORS requests (see detailed cors spec).

In the nutshell Simple request is GET, HEAD or POST methods without special headers. In this case request looks like this:

Simle CORS request

and our Nginx config snippet to handle simple requests:

  if ($request_method ~* "(GET|POST)") {
      add_header "Access-Control-Allow-Origin"  *;
  }

If the request involves PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH methods or any special headers not listed for the Simple Request ( see the spec link I gave above ), then it’s treated as Preflighted request. Don’t be scared by fancy words here, in case of preflighted request the client needs to send two requests:

  1. OPTIONS request first to verify what’s allowed. Here is our Nginx config part for that:
    if ($request_method = OPTIONS ) {
      add_header "Access-Control-Allow-Origin"  *;
      add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD";
      add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept";
      return 200;
    }
    
  2. Once the client receives the response and checks that original request is allowed. It issues second request with original data.

Here is the diagram to show requests flow:

Preflighted request flow, Nginx access-control-allow-origin

Here are a couple useful CURL command that I use to test the implementation:

curl -s -D - -H "Origin: http://example.com" https://api.example.com/my-endpoint -o /dev/null

You should see Access-Control-Allow-Origin header if everything look good.

To test Preflighted requests, just add -X OPTIONS like this:

curl -s -D - -H "Origin: http://example.com" -X OPTIONS https://api.example.com/my-endpoint -o /dev/null

If you want dive deeper into Nginx access control allow origin and CORS here is excellent post that I already mentioned before – https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Want faster website?

I'm Sergey Khaladzinski (@gansbrest). I regularly share tips and tricks to improve websites performance. My main focus at the moment is monitoring, servers and backend performance. If you liked this article, join my list to get updates from me in the future.

Directly from me. No spam guaranteed. Unsubscribe at any time. Powered by ConvertKit