CORS on Nginx

The following Nginx configuration enables CORS, with support for preflight requests.

Wide-open CORS config for nginx

location / {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # Custom headers and headers various browsers *should* be OK with but aren't
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # 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;
        return 204;
    }
    if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    }
    if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    }
}
Source: Michiel Kalkman, https://michielkalkman.com/snippets/nginx-cors-open-configuration.html

Origin-specified CORS config for nginx

Replace all $http_origin with $scheme://$host if you need to compatibility with Edge Legacy or Firefox <69.

location / {
    if ($allow_cors = OPTIONS) {
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin#cors_and_caching
        add_header 'Vary' 'Origin' always;
        add_header 'Access-Control-Allow-Origin' $http_origin;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # Custom headers and headers various browsers *should* be OK with but aren't
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # Tell client that this pre-flight info is valid for 20 days
        #
        add_header 'Access-Control-Max-Age' 86400;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }
    if ($allow_cors = GET|POST) {
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin#cors_and_caching
        add_header 'Vary' 'Origin' always;
        add_header 'Access-Control-Allow-Origin' $http_origin always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    }
}
    

The following directive must be placed under http {} level

map '$request_method $http_origin' $allow_cors {
    '~^OPTIONS https://(allow\.first\.domain|allow\.second\.domain)'    OPTIONS;
    '~^(GET|POST) https://(allow\.first\.domain|allow\.second\.domain)' GET|POST;
    default 0;
}
    

Who’s behind this

Monsur Hossain and Michael Hausenblas

Contribute

The content on this site stays fresh thanks to help from users like you! If you have suggestions or would like to contribute, fork us on GitHub.

Buy the book

Save 39% on CORS in Action with promotional code hossainco at manning.com/hossain