¿Es imposible tener encabezados condicionales en Nginx?

¿Es imposible tener encabezados condicionales en Nginx?

Actualmente estoy intentando devolver solo un conjunto de encabezados CORS de forma condicional usando Nginx. Al principio parecía una tarea sencilla, ya que ya tenía esta configuración funcional:

upstream api-app {
  server unix:/tmp/api-app.sock fail_timeout=0;
}

server {
  listen 80;
  # [other server stuff...]

  # CORS headers added to all ALL responses of this server (needed for all requests)
  more_set_headers 'Access-Control-Allow-Methods: GET,POST,PATCH,PUT,DELETE,OPTIONS';
  more_set_headers 'Access-Control-Allow-Origin: *';
  more_set_headers 'Access-Control-Allow-Credentials: true';
  more_set_headers 'Access-Control-Request-Method: GET,POST,PATCH,PUT,DELETE,OPTIONS';
  more_set_headers 'Access-Control-Request-Headers: Content-Type';
  more_set_headers 'Access-Control-Allow-Headers: Origin,X-Requested-With,Content-Type,Accept,Session-Id,Role-Id,Visitor-Id,X-Window-Location';
  more_set_headers 'Access-Control-Expose-Headers: X-Total-Entries,X-Total-Pages,X-Page,X-Per-Page,X-Folder-Hierarchy';

  location / {
    # For OPTIONS request only return above headers and no content (also allow caching them)
    if ($request_method = 'OPTIONS') {
      # cache above (Access-Control) headers
      add_header 'Access-Control-Max-Age' 600;

      # set content length and type just for good measure:
      add_header 'Content-Length' 0;
      add_header 'Content-Type' 'text/plain charset=UTF-8';

      # return 204 - no content
      return 204;
    }

    # Forward requests to actual app (if all above conditions did not match)
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_read_timeout 35;
    proxy_send_timeout 35;

    proxy_pass http://api-app;
  }
}

Entonces solo quería cambiar la sección del encabezado CORS a algo. como esto

# ...
  set $cors '';

  if ($http_origin ~ '^https?://(some.domain|some.other.domain|other-allows.domain)') {
      set $cors 'true';
  }

  if ($cors = 'true') {
      more_set_headers 'Access-Control-Allow-Methods: GET,POST,PATCH,PUT,DELETE,OPTIONS';
      more_set_headers 'Access-Control-Allow-Origin: $http_origin';
      more_set_headers 'Access-Control-Allow-Credentials: true';
      more_set_headers 'Access-Control-Request-Method: GET,POST,PATCH,PUT,DELETE,OPTIONS';
      more_set_headers 'Access-Control-Request-Headers: Content-Type';
      more_set_headers 'Access-Control-Allow-Headers: Origin,X-Requested-With,Content-Type,Accept,Session-Id,Role-Id,Visitor-Id,X-Window-Location';
      more_set_headers 'Access-Control-Expose-Headers: X-Total-Entries,X-Total-Pages,X-Page,X-Per-Page,X-Folder-Hierarchy';
  }
  

  location / {
# ...

Sin embargo, nginx -trápidamente me dijeron more_set_headersque no se puede usar en una declaración if. Tampoco puede add_header.

Descubrí que en la locationsección funcionaría. Pero ya sabía que esta no será una buena solución, como todos sabemos.si es malo en las secciones de ubicación de nginx. Y tenía razón, ya que entonces los encabezados CORS se perderían para la solicitud de OPCIONES, ya tenía un caso if (uno correcto, como regresa).

También encontré la opción de usar Nginxs.función de mapa. Pero eso sólo funciona si quiero modificar los valores de los encabezados, no si quiero agregar un bloque completo de encabezados.

Entonces mi pregunta es la siguiente: ¿Hay alguna manera de agregar encabezados condicionalmente (sin usar el mapa) y fuera del bloque de ubicación? Lo ideal sería permitir la inclusión de la sección CORS para poder usarla en múltiples servidores (es decir, sitios nginx).

Respuesta1

No sé si more_set_headersacepta cadenas vacías. Si acepta, entonces puede definir varias mapdeclaraciones:

map $http_origin $cors_methods {
    default "";
    ~^https?://(some.domain|some.other.domain|other-allows.domain) Access-Control-Allow-Methods: GET,POST,PATCH,PUT,DELETE,OPTIONS;
}

map $http_origin $cors_origin {
    default "";
    ~^https?://(some.domain|some.other.domain|other-allows.domain) Access-Control-Allow-Origin: $http_origin;
}

Y luego usa esto:

more_set_headers $cors_methods;
more_set_headers $cors_origin;

información relacionada