Using CORS with Nginx

2020-01-21

Coffee in the making

We have had the opportunity to work on a lot of frontend projects over the years at Will & Skill and one of the things that often come around in every project is how You handle CORS requests.

We have sometimes gone with the django package django-cors-headers while developing but that does not feel right in production. We would rather handle the CORS headers directly in Nginx.

Below is a snippet that You can use in Your project to offload CORS request handling to Nginx.

Javascriptif ($request_method = 'GET') {
    # 1. Allow any origin
    add_header 'Access-Control-Allow-Origin' '*' always;
    # 2. Credentials can be cookies, authorization headers or TLS client certificates
    add_header 'Access-Control-Allow-Credentials' 'true';
    # 3. What methods should be allowed when accessing the resource in response to a preflight request
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
    # 4. Access-Control-Allow-Headers response header is used in response to a preflight request to indicate which HTTP headers can be used during the actual request.
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
} 
  1. Read more on Access-Control-Allow-Origin

  2. Read more on Access-Control-Allow-Credentials

  3. Read more on Access-Control-Allow-Methods

  4. Read more on Access-Control-Allow-Headers

A more complete configuration can be found below. You will have to customize the snippet below to meet Your needs when it comes HTTPS and so on.

server {
    listen 80;
    server_name www.example.com;
    
    #
    # Your configuration here...
    #

    location / {
        if ($scheme = http) {
            return 301 https://$server_name$request_uri;
        }        
        add_header Cache-Control private;
        add_header Cache-Control no-cache;
        add_header Cache-Control no-store;
        add_header Cache-Control must-revalidate;
        add_header Pragma no-cache;
        uwsgi_pass unix:///tmp/uwsgi.sock;
        include    /etc/nginx/uwsgi_params;
        proxy_read_timeout 1800;
        uwsgi_read_timeout 1800;

        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' '*' always;
            #
            # Om nom nom cookies
            #
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
            #
            # Custom headers and headers various browsers *should* be OK with but aren't
            #
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
            #
            # Tell client that this pre-flight info is valid for 20 days
            #
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain charset=UTF-8';
            add_header 'Content-Length' 0;
            add_header Access-Control-Allow-Headers "Authorization";
            add_header Access-Control-Allow-Credentials "true";
            return 204;
        }
        if ($request_method = 'PUT') {
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        }
        if ($request_method = 'PATCH') {
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        }
        if ($request_method = 'POST') {
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        }
        if ($request_method = 'GET') {
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        }        
    }
}

Happy coding!