Настройка CORS в Nginx

CORS (cross-origin resource sharing) — совместное использование ресурсов между разными источниками. Это спецификация, которая обеспечивает действительно открытый доступ между доменами. Если вы размещаете общедоступный контент, рассмотрите возможность использования CORS, чтобы открыть его для универсального доступа из JavaScript в браузерах.


До недавнего времени основным способом преодоления ограничений, наложенных в same-origin-policy относительно XSS запросов, было использование JSONP. Сам JSONP имеет неустранимое ограничение — позволяет только получение данных GET методом, то есть отправка данных через POST метод остается недоступной.

Почему CORS важен?

За последние годы JavaScript и веб-программирование активно развиваются, но same-origin политику никто не отменял. Это препятствует тому, чтобы JavaScript делал запросы между разными доменами, что породило различные хаки для выполнения междоменных запросов.

CORS представляет стандартный механизм, который может использоваться всеми браузерами для реализации междоменных запросов. Спецификация определяет набор заголовков, которые позволяют браузеру и серверу сообщать о том, какие запросы разрешены (и не разрешены). CORS продолжает дух открытой сети, предоставляя доступ к API всем.

Правило ограничения домена (Same Origin Policy, Принцип одинакового источника) — это важная концепция безопасности для некоторых языков программирования на стороне клиента, таких как JavaScript. Политика разрешает сценариям, находящимся на страницах одного сайта, доступ к методам и свойствам друг друга без ограничений, но предотвращает доступ к большинству методов и свойств для страниц на разных сайтах. Одинаковые источники — это источники, у которых совпадают три признака: домен, порт и протокол.

Концепция правила ограничения домена появилась во времена Netscape Navigator 2.0. Скрытые производные оригинальной разработки используются во всех современных браузерах, а также в плагинах, таких как Adobe Flash либо для механизмов отличных от DOM манипуляций, таких как XMLHttpRequest.

Как это работает?

ля инициации Cross-origin запроса браузер клиента добавляет в HTTP запрос Origin (домен сайта, с которого происходит запрос). Например страница http://www.a.com/page.html пытается получить данные со страницы http://www.b.com/cors.txt. В случае если браузер клиента поддерживает технологию CORS, запрос будет выглядеть так:

GET /cors.txt HTTP/1.1
Host: www.b.com
Origin: www.a.com

Если сервер www.b.com хочет разрешить получение данных с www.a.com то в ответе сервера будет присутствовать строчка:

Access-Control-Allow-Origin: http://www.a.com

Если в ответе сервера отсутствует данная строка, то браузер поддерживающий технологию CORS, передаст ошибку вместо данных.

В случае, если сервер хочет разрешить доступ любому домену, он может указать в ответе:

Access-Control-Allow-Origin: *

Если сервер хочет разрешить доступ более чем одному домену, то в ответе сервера должно быть по одной строчке Access-Control-Allow-Origin для каждого домена.

Access-Control-Allow-Origin: http://www.a.com
Access-Control-Allow-Origin: http://www.b.com
Access-Control-Allow-Origin: http://www.c.com

На практике чаще используется запись из нескольких доменов, разделенных пробелом:

 Access-Control-Allow-Origin: http://www.a.com http://www.b.com http://www.c.com

Пример конфигурации CORS для web-сервера 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' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
}