CORS и обработка 413й ошибки в бэкенде

Была у меня задача помочь с перехватом ошибки 413 у nginx.

Есть фронт, есть бэкенд. И при загрузке файла размером превышающим значение в настройках nginx возвращался пустой Responce и ошибку:

Access to XMLHttpRequest at 'https://backend-develop.dev.mydomain.su/v1/mydomain/image/' from origin 'https://front-develop.dev.mydomain.su' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Вроде понятно, ошибка связана с CORS: нет хэдера на запрашиваемом ресурсе, чтобы пришла ответочка фронту. Но это оказалось не причиной, а следствием, т.к. CORS уже был настроен.

Дело в том, что ошибка 413  (Request Entity Too Large) не обрабатывалась бэкендом по одной простой причине: её обрабатывал nginx и она обрабатывается по умолчанию без Headers.

Чтобы Headers появились и передавались, надо добавить вот такую настройку:

add_header 'Access-Control-Allow-Origin' '*' always;

Но это не срабатывало. И дело тут не в кавычках, кстати говоря. Эта настройка добавляется в блок location и добавлял я её в блок бэкенду: https://backend-develop.dev.mydomain.su. Так не работало. И сработало вот так:

error_page 413 = @json413error;

location @json413error {
return 413;
add_header 'Access-Control-Allow-Origin' '*' always;
        }

Такой блок добавляется не в location а в директиву server нужного хоста (в моём случае это https://backend-develop.dev.mydomain.su. И вот тогда случается «магия». Nginx обрабатывает ошибку 413, перехватывая её на (внезапно) всё ту же 413ю, но при этом отдельно опцией награждает запрос Headers, что даёт возможность бэкенду отдать Response.


Для того, чтобы проверить и разрешить всё по CORS, я использовал вот такой конфиг (добавляется в блок location):

if ($request_method = 'OPTIONS') {
   add_header 'Access-Control-Allow-Origin' '*' always;
   add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
   #
   # Custom headers and headers various browsers *should* be OK with but aren't
   #
   add_header 'Access-Control-Allow-Headers' 'Origin,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Accept,Authorization,x-custom-environment';
   add_header 'Access-Control-Allow-Credentials' 'true';
   #
   # 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-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
   add_header 'Access-Control-Allow-Headers' 'Origin,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Accept,Authorization,x-custom-environment' always;
   add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
   add_header 'Access-Control-Allow-Credentials' 'true';
}
if ($request_method = 'GET') {
   add_header 'Access-Control-Allow-Origin' '*' always;
   add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
   add_header 'Access-Control-Allow-Headers' 'Origin,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Accept,Authorization,x-custom-environment' always;
   add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
   add_header 'Access-Control-Allow-Credentials' 'true';
}

#Ещё по теме, что я ковырял

Access to XMLHttpRequest at 'https://backend-develop.dev.mydomain.su/v1/mydomain/metadata/language/' from origin 'https://front-develop.dev.mydomain.su' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values 'https://front-develop.dev.mydomain.su, *', but only one is allowed.

Эта ошибка говорит о том, что CORS прописаны в нескольких местах. Например, есть настройки в коде приложения (в бэкенде) и прописаны в nginx для того же бэкенда. Так не работает. Надо устанавливать настройки только в одном месте (либо чтобы их правила не пересекались).


response body is not available to script Reason CORS Missing Allow Origin


https://enable-cors.org/server_nginx.html

#
# 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-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;
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-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;
     }
}

 


Ошибка

Request header field x-custom-environment is not allowed by Access-Control-Allow-Headers in preflight response

Эта ошибка говорит о том, что в настройках нет хэдера x-custom-environment (это нестандартная кастомная переменная). Ниже представлена настройка, где эта переменная прописана.

server {
    listen 80;
    server_name your_domain.com;

    location / {
        # Разрешаем кастомный заголовок в предварительных (preflight) запросах
        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,Authorization,x-custom-environment';
        
        if ($request_method = OPTIONS) {
            return 204;
        }

        # Другие настройки...
    }

    # Другие настройки сервера...
}

Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

 


Vary: Origin — это заголовок, который говорит браузерам и кэширующим прокси, что ответ может зависеть от значения заголовка Origin в запросе. Это полезно для правильной работы кэширования, когда ресурсы отдаются в зависимости от источника запроса.

В вашем случае, у вас уже присутствует заголовок Access-Control-Allow-Origin, который явно указывает разрешенный источник. В этом контексте, использование Vary: Origin не является обязательным, так как Access-Control-Allow-Origin уже предоставляет информацию о том, какие источники разрешены.

Тем не менее, добавление Vary: Origin может быть полезным, если у вас есть другие части приложения, которые могут предоставлять разные ответы в зависимости от Origin. В таком случае Vary: Origin может помочь в правильной обработке кэширования.

Если вам не требуется учитывать Origin при кэшировании или других случаях, то вы можете не добавлять Vary: Origin. Однако, если у вас есть различия в ответах в зависимости от источника запроса и вы хотите, чтобы браузеры и кэширование правильно учитывали эти различия, добавление Vary: Origin может быть разумным.


#Описание конфига nginx:

add_header Strict-Transport-Security $sts_header always; в конфигурации Nginx отвечает за установку заголовка Strict-Transport-Security (HSTS).

HSTS — это механизм безопасности, предназначенный для защиты от атак типа Man-in-the-Middle, включая атаки через протоколы, такие как SSL/TLS. Заголовок HSTS сообщает браузерам, что соединение должно быть устанавливаться только через HTTPS, даже если пользователь вводит URL без указания протокола.

Давайте разберемся с различными частями данной строки:

  • add_header Strict-Transport-Security: Это директива Nginx, которая добавляет HTTP-заголовок Strict-Transport-Security.
  • $sts_header: Это переменная, которая, вероятно, предварительно определена в вашей конфигурации и содержит значение, которое должно быть установлено в заголовок Strict-Transport-Security.
  • always: Это аргумент, указывающий, что заголовок должен быть установлен всегда, даже если сервер возвращает ошибку (например, 4xx или 5xx). Это важно для того, чтобы предотвратить атаки сбора данных вроде SSL-strip.

Типичный пример значения для $sts_header может выглядеть так:

set $sts_header "max-age=31536000; includeSubDomains; preload";

В данном примере max-age=31536000 указывает, что браузеры должны запомнить, что сайт должен быть доступен только через HTTPS в течение одного года (в секундах). includeSubDomains указывает, что эта политика применяется также ко всем поддоменам. preload сообщает, что сайт должен быть включен в списки загрузки HSTS браузеров, чтобы браузеры знали о его политике HSTS еще до первого визита пользователя.


#—qqq
proxy_intercept_errors on;
error_page 413 = @json413error;
location @json413error {
        default_type application/json;
        return 200 ‘{«errors»: «server is not up»}’;
try_files /path/to/error.json =404;
}
#—
#—qqq
proxy_intercept_errors on;
error_page 413 = @json413error;
location @json413error {
        default_type application/json;
        return 200 ‘{«errors»: «server is not up»}’;
        add_header ‘Access-Control-Allow-Origin’ ‘*’ always;
                }
#—

Перехват страницы ошибки и отдача json ответа — https://www.jajaldoang.com/post/nginx-custom-error-response/

Nginx и хэдеры — https://nginx.org/en/docs/http/ngx_http_headers_module.html

Конфигурация nginx для CORS — https://enable-cors.org/server_nginx.html

Добавить комментарий