nginx config to enable CORS with origin matching

Slava Fomin II picture Slava Fomin II · Jan 22, 2019 · Viewed 10k times · Source

I've tried to use a very popular config for nginx, which enables CORS and supports origin matching using regular expressions.

Here's my config:

server {
    listen 80 default_server;
    root /var/www;

    location / {
        if ($http_origin ~ '^http://(www\.)?example.com$') {
            add_header Access-Control-Allow-Origin "$http_origin";
        }

        # Handling preflight requests
        if ($request_method = OPTIONS) {
            add_header Content-Type text/plain;
            add_header Content-Length 0;
            return 204;
        }
    }
}

However, this config must use two conditions: one to match the origin domain name and another one to capture preflight requests. So when the second condition is matched, the headers from the first conditions are not added to the response.

According to the If Is Evil official article, this is an expected behavior for nginx.

If If Is Evil how do I enable CORS in nginx then? Or maybe there is a way to overcome this limitation somehow?

Answer

Ivan Shatsky picture Ivan Shatsky · Jan 22, 2019

You can try to use map istead of the first if block:

map $http_origin $allow_origin {
    ~^http://(www\.)?example.com$ $http_origin;
}
map $http_origin $allow_methods {
    ~^http://(www\.)?example.com$ "OPTIONS, HEAD, GET";
}

server {
    listen 80 default_server;
    root /var/www;

    location / {
        add_header Access-Control-Allow-Origin $allow_origin;
        add_header Access-Control-Allow-Methods $allow_methods;

        # Handling preflight requests
        if ($request_method = OPTIONS) {
            add_header Content-Type text/plain;
            add_header Content-Length 0;
            return 204;
        }
    }
}

nginx will refuse to add an empty HTTP headers, so they will be added only if Origin header is present in request and matched this regex.