Entendiendo el proxy HTTP de Nginx, balanceo de carga, almacenamiento en búfer y almacenamiento en caché

Introducción

En esta guía, analizaremos las capacidades del proxy http de Nginx, que permiten a Nginx pasar solicitudes a servidores http de back-end para su posterior procesamiento. Nginx a menudo se configura como una solución de proxy inverso para ayudar a escalar la infraestructura o para pasar solicitudes a otros servidores que no están diseñados para manejar grandes cargas de clientes.

En el camino, analizaremos cómo escalar utilizando las capacidades de balanceo de carga integradas de Nginx. También exploraremos el almacenamiento en búfer y el almacenamiento en caché para mejorar el rendimiento de las operaciones de proxy para los clientes.

Información general de proxy

Si solo has utilizado servidores web en el pasado para configuraciones simples de un solo servidor, es posible que te estés preguntando por qué tendrías que realizar solicitudes de proxy.

Una de las razones para hacer proxy a otros servidores de Nginx es la capacidad de escalar tu infraestructura. Nginx está diseñado para manejar muchas conexiones simultáneas al mismo tiempo. Esto lo hace ideal para ser el punto de contacto para los clientes. El servidor puede pasar solicitudes a cualquier número de servidores backend para manejar la mayor parte del trabajo, que distribuye la carga en su infraestructura. Este diseño también te brinda flexibilidad para agregar fácilmente servidores de back-end o eliminarlos según sea necesario para el mantenimiento.

Otra instancia en la que un proxy http puede ser útil es cuando utiliza sservidores de aplicaciones que pueden no estar diseñados para manejar solicitudes directamente de clientes en entornos de producción. Muchos frameworks incluyen servidores web, pero la mayoría de ellos no son tan robustos como los servidores diseñados para un alto rendimiento como Nginx. Poner a Nginx frente a estos servidores puede llevar a una mejor experiencia para los usuarios y una mayor seguridad.

La asignación de datos en Nginx se realiza mediante la manipulación de una solicitud dirigida al servidor Nginx y pasándola a otros servidores para el procesamiento real. El resultado de la solicitud se devuelve a Nginx, que luego transmite la información al cliente. Los otros servidores en este caso pueden ser máquinas remotas, servidores locales o incluso otros servidores virtuales definidos dentro de Nginx. Los servidores a los que los proxies Nginx solicitan se conocen como servidores ascendentes.

Nginx puede enviar solicitudes a servidores que se comunican mediante los protocolos http (s), FastCGI, SCGI y uwsgi, o memcached a través de conjuntos de directivas independientes para cada tipo de proxy. En esta guía, nos centraremos en el protocolo http. La instancia de Nginx es responsable de pasar la solicitud y masajear los componentes del mensaje en un formato que el servidor de nivel superior pueda entender.

Deconstruir un Proxy Pass HTTP básico

El tipo de proxy más directo implica la entrega de una solicitud a un solo servidor que puede comunicarse mediante http. Este tipo de proxy se conoce como un ” proxy pass” genérico y se maneja mediante la directiva proxy_pass con nombre apropiado.

La directiva proxy_pass se encuentra principalmente en contextos de ubicación. También es válido en bloques if dentro de un contexto de ubicación y en contextos limit_except. Cuando una solicitud coincide con una ubicación con una directiva proxy_pass en el interior, la solicitud se reenvía a la URL proporcionada por la directiva.

Echemos un vistazo a un ejemplo:

En el fragmento de configuración anterior, no se proporciona un URI al final del servidor en la definición proxy_pass. Para las definiciones que se ajustan a este patrón, el URI solicitado por el cliente se pasará al servidor ascendente tal como está.

Por ejemplo, cuando este bloque /match/here/please maneja una solicitud, el URI de la solicitud se enviará al servidor example.com como http://example.com/match/here/please.

Echemos un vistazo al escenario alternativo:

En el ejemplo anterior, el servidor proxy se define con un segmento URI en el final (/new/prefix). Cuando se da un URI en la definición proxy_pass, la porción de la solicitud que coincide con la definición de ubicación se reemplaza por este URI durante el pass.

Por ejemplo, una solicitud para /match/here/please en el servidor Nginx se pasará al servidor ascendente como http://example.com/new/prefix/please/match/here es reemplazado por /new/prefix. Este es un punto importante a tener en cuenta.

A veces, este tipo de reemplazo es imposible. En estos casos, el URI al final de la definición proxy_pass se ignora y el URI original del cliente o el URI modificado por otras directivas se pasarán al servidor ascendente.

Por ejemplo, cuando la ubicación coincide con expresiones regulares, Nginx no puede determinar qué parte del URI coincide con la expresión, por lo que envía el URI original de la solicitud del cliente. Otro ejemplo es cuando se usa una directiva de reescritura dentro de la misma ubicación, lo que hace que el URI del cliente se vuelva a escribir, pero aún se maneje en el mismo bloque. En este caso, se pasará el URI reescrito.

Entendiendo cómo Nginx procesa los encabezados

Una cosa que podría no estar clara de inmediato es que es importante pasar más que solo el URI si esperas que el servidor de nivel superior maneje la solicitud correctamente. La solicitud proveniente de Nginx en nombre de un cliente se verá diferente a una solicitud que provenga directamente de un cliente. Una gran parte de esto son los encabezados que acompañan la solicitud.

Cuando Nginx envía un proxy a una solicitud, automáticamente realiza algunos ajustes a los encabezados de solicitud que recibe del cliente:

  • El servidor Nginx se deshace de cualquier encabezado vacío. No hay ninguna opción de pasar valores vacíos a otro servidor; Sólo serviría para inflar la petición.
  • Nginx, de forma predeterminada, considerará cualquier encabezado que contenga guiones bajos como no válido. Se eliminarán estos de la solicitud de proxy. Si deseas que Nginx los interprete como válidos, puedes establecer la directiva underscores_in_headers en “on”, de lo contrario, sus encabezados nunca llegarán al servidor backend.
  • El encabezado “Host” se vuelve a escribir en el valor definido por la variable $proxy_host. Esta será la dirección IP o el nombre y el número de puerto del canal ascendente, directamente como se define en la directiva proxy_pass.
  • El encabezado “Connection” se cambia a “close”. Este encabezado se utiliza para señalar información sobre la conexión particular establecida entre dos partes. En este caso, Nginx configura esto como “close” para indicar al servidor ascendente que esta conexión se cerrará una vez que se responda a la solicitud original. El ascendente no debe esperar que esta conexión sea persistente.

El primer punto que podemos extrapolar de lo anterior es que cualquier encabezado que no desees que se apruebe debe establecerse en una cadena vacía. Los encabezados con valores vacíos se eliminan completamente de la solicitud pasada.

El siguiente punto a partir de la información anterior es que, si tu aplicación backend procesará encabezados no estándar, debes asegurarte de que no tengan guiones bajos. Si necesitas encabezados que usen un guión bajo, puedes configurar la directiva underscores_in_headers a “on” en tu configuración (válida ya sea en el contexto http o en el contexto de la declaración del servidor predeterminado para la combinación de dirección IP/puerto). Si no lo haces, Nginx marcará estos encabezados como no válidos y los silenciará antes de pasar a tu ascendente.

El encabezado “Host” es de particular importancia en la mayoría de los escenarios de proxy. Como se indicó anteriormente, de forma predeterminada, esto se establecerá en el valor de $proxy_host, una variable que contendrá el nombre de dominio o la dirección IP y el puerto tomado directamente de la definición proxy_pass. Esto se selecciona de forma predeterminada, ya que es la única dirección en la que Nginx puede estar seguro de que el servidor ascendente responde (ya que se extrae directamente de la información de conexión).

Los valores más comunes para el encabezado “Host” se encuentran a continuación:

  • $proxy_host:

Esto establece el encabezado “Host” en el nombre de dominio o la dirección IP y el combo de puerto tomados de la definición proxy_pass. Este es el valor predeterminado y “safe” desde la perspectiva de Nginx, pero no suele ser lo que necesita el servidor proxy para manejar la solicitud correctamente.

  • $http_host:

Establece el encabezado “Host” en el encabezado “Host” de la solicitud del cliente. Los encabezados enviados por el cliente siempre están disponibles en Nginx como variables. Las variables comenzarán con un prefijo $http_, seguido del nombre del encabezado en minúsculas, con cualquier guión reemplazado por guiones bajos. Aunque la variable $http_host funciona la mayor parte del tiempo, cuando la solicitud del cliente no tiene un encabezado “Host” válido, esto puede hacer que el pase falle.

  • $host:

Esta variable se establece, en orden de preferencia a: el nombre de host de la línea de solicitud, el encabezado “Host” de la solicitud del cliente o el nombre del servidor que coincide con la solicitud.

En la mayoría de los casos, desearás establecer el encabezado “Host” en la variable $host. Es el más flexible y normalmente proporcionará a los servidores proxy un encabezado “Host” completo con la mayor precisión posible.

Establecer o restablecer encabezados

Para ajustar o establecer encabezados para conexiones proxy, podemos usar la directiva proxy_set_header. Por ejemplo, para cambiar el encabezado “Host” como hemos discutido, y agregar algunos encabezados adicionales comunes con las solicitudes de proxy, podríamos usar algo como esto:

La solicitud anterior establece el encabezado “Host” en la variable $host, que debe contener información sobre el host original que se solicita. El encabezado X-Forwarded-Proto le proporciona al servidor proxy información sobre el esquema de la solicitud del cliente original (ya sea una solicitud http o https).

X-Real-IP se establece en la dirección IP del cliente para que el proxy pueda tomar decisiones correctamente o iniciar sesión basándose en esta información. El encabezado X-Forwarded-For es una lista que contiene las direcciones IP de todos los servidores a los que el cliente ha sido enviado hasta este punto. En el ejemplo anterior, configuramos esto a la variable $proxy_add_x_forwarded_for. Esta variable toma el valor del encabezado X-Forwarded-For original recuperado del cliente y agrega la dirección IP del servidor Nginx al final.

Por supuesto, podríamos mover las directivas proxy_set_header al servidor o al contexto http, permitiendo que se haga referencia en más de una ubicación:

Definición de un contexto ascendente para el balanceo de carga de las conexiones de proxy

En los ejemplos anteriores, demostramos cómo hacer un proxy http simple a un único servidor backend. Nginx nos permite escalar fácilmente esta configuración especificando grupos completos de servidores backend a los que podemos pasar solicitudes.

Podemos hacer esto usando la directiva upstream para definir un grupo de servidores. Esta configuración asume que cualquiera de los servidores listados es capaz de manejar la solicitud de un cliente. Esto nos permite escalar nuestra infraestructura casi sin esfuerzo. La directiva upstream debe establecerse en el contexto http de tu configuración Nginx.

Veamos un ejemplo simple:

En el ejemplo anterior, hemos configurado un contexto ascendente llamado backend_hosts. Una vez definido, este nombre estará disponible para su uso dentro de proxy passes (pases de proxy) como si fuera un nombre de dominio regular. Como puedes ver, dentro de nuestro bloque de servidores pasamos cualquier solicitud hecha al grupo example.com/proxy-me/… que definimos anteriormente. Dentro de ese grupo, se selecciona un host aplicando un algoritmo configurable. Por defecto, esto es solo un simple proceso de selección por turnos (cada solicitud se enrutará a un host diferente).

Cambiando el algoritmo de balanceo ascendente

Puedes modificar el algoritmo de balanceo usado por el grupo ascendente incluyendo directivas o indicadores dentro del contexto ascendente:

  • (round robin):

el algoritmo de balanceo de carga predeterminado que se utiliza si no hay otras directivas de equilibrio presentes. Cada servidor definido en el contexto ascendente se pasa las solicitudes de forma secuencial.

  • least_conn:

Especifica que siempre se deben dar nuevas conexiones al backend que tenga el menor número de conexiones activas. Esto puede ser especialmente útil en situaciones donde las conexiones al backend pueden persistir por algún tiempo.

  • ip_hash:

Este algoritmo de balanceo distribuye las solicitudes a diferentes servidores en función de la dirección IP del cliente. Los primeros tres octetos se utilizan como una clave para decidir sobre el servidor para manejar la solicitud. El resultado es que los clientes tienden a ser atendidos por el mismo servidor cada vez, lo que puede ayudar en la consistencia de la sesión.

  • hash:

Este algoritmo de balanceo se utiliza principalmente con el proxy memcached. Los servidores se dividen en función del valor de una clave hash proporcionada de forma arbitraria. Esto puede ser texto, variables o una combinación. Este es el único método de balanceo que requiere que el usuario proporcione datos, que es la clave que debe usarse para el hash.

Al cambiar el algoritmo de balanceo, el bloque puede verse algo como esto:

En el ejemplo anterior, el servidor se seleccionará en función de cuál de ellos tenga menos conexiones. La directiva ip_hash podría establecerse de la misma manera para obtener una cierta cantidad de “pegajosidad” de sesión.

En cuanto al método hash, debes proporcionar la clave contra el hash. Esto puede ser lo que desees:

El ejemplo anterior distribuirá las solicitudes según el valor de la dirección IP y el puerto del cliente. También agregamos el parámetro opcional consistent, que implementa el algoritmo de hashing coherente de ketama. Básicamente, esto significa que, si cambian los servidores anteriores, habrá un impacto mínimo en su caché.

Configuración del peso del servidor para el balanceo

En las declaraciones de los servidores de back-end, de forma predeterminada, cada servidor está igualmente “ponderado”. Esto supone que cada servidor puede y debe manejar la misma cantidad de carga (teniendo en cuenta los efectos de los algoritmos de balanceo). Sin embargo, también puedes establecer un peso alternativo para los servidores durante la declaración:

En el ejemplo anterior, host1.example.com recibirá tres veces el tráfico que los otros dos servidores. Por defecto, a cada servidor se le asigna uno.

Uso de buffers para liberar servidores backend

Un problema con el proxy que afecta a muchos usuarios es el impacto en el rendimiento de agregar un servidor adicional al proceso. En la mayoría de los casos, esto se puede mitigar en gran medida aprovechando las capacidades de almacenamiento en caché y almacenamiento en búfer de Nginx.

Cuando se dirija a otro servidor, la velocidad de dos conexiones diferentes afectará la experiencia del cliente:

  • La conexión del cliente al proxy Nginx.
  • La conexión del proxy Nginx al servidor backend.

Nginx tiene la capacidad de ajustar su comportamiento en función de cualquiera de estas conexiones que desees optimizar.

Sin búferes, los datos se envían desde el servidor proxy e inmediatamente comienzan a transmitirse al cliente. Si se supone que los clientes son rápidos, el almacenamiento en búfer se puede desactivar para enviar los datos al cliente tan pronto como sea posible. Con los buffers, el proxy Nginx almacenará temporalmente la respuesta del backend y luego enviará estos datos al cliente. Si el cliente es lento, esto permite que el servidor Nginx cierre la conexión con el backend antes. Luego, puede manejar la distribución de los datos al cliente al ritmo que sea posible.

Nginx utiliza por defecto un diseño de almacenamiento en búfer, ya que los clientes tienden a tener velocidades de conexión muy diferentes. Podemos ajustar el comportamiento del buffering con las siguientes directivas. Estos se pueden configurar en los contextos http, servidor o ubicación. Es importante tener en cuenta que las directivas de tamaño se configuran por solicitud, por lo que aumentarlas más allá de tu necesidad puede afectar su rendimiento cuando hay muchas solicitudes de clientes:

  • proxy_buffering:

Esta directiva controla si el almacenamiento en búfer para este contexto y los contextos secundarios están habilitados. Por defecto, esto está “on”.

  • proxy_buffers:

Esta directiva controla el número (primer argumento) y el tamaño (segundo argumento) de los buffers para las respuestas de proxy. El valor predeterminado es configurar 8 buffers de un tamaño igual a una página de memoria (ya sea 4k o 8k). Aumentar el número de búferes puede permitirte almacenar más información.

  • proxy_buffer_size:

La parte inicial de la respuesta de un servidor backend, que contiene encabezados, se almacena en un búfer por separado del resto de la respuesta. Esta directiva establece el tamaño del búfer para esta parte de la respuesta. De forma predeterminada, tendrá el mismo tamaño proxy_buffers, pero como se usa para información de encabezado, por lo general se puede establecer en un valor más bajo.

  • proxy_busy_buffers_size:

Esta directiva establece el tamaño máximo de los búferes que pueden marcarse como “listo para el cliente” y, por lo tanto, ocupado. Mientras que un cliente solo puede leer los datos de un búfer a la vez, los búferes se colocan en una cola para enviar al cliente en grupos. Esta directiva controla el tamaño del espacio de búfer permitido en este estado.

  • proxy_max_temp_file_size:

Este es el tamaño máximo, por solicitud, para un archivo temporal en el disco. Estos se crean cuando la respuesta ascendente es demasiado grande para caber en un búfer.

  • proxy_temp_file_write_size:

Esta es la cantidad de datos que Nginx escribirá en el archivo temporal a la vez cuando la respuesta del servidor proxy es demasiado grande para los búferes configurados.

  • proxy_temp_path:

Esta es la ruta al área en el disco donde Nginx debe almacenar cualquier archivo temporal cuando la respuesta del servidor ascendente no puede caber en los buffers configurados.

Como puedes ver, Nginx proporciona varias directivas diferentes para modificar el comportamiento del almacenamiento en búfer. La mayoría de las veces, no tendrás que preocuparte por la mayoría de estos, pero puede ser útil ajustar algunos de estos valores. Probablemente las más útiles para ajustar son las directivas proxy_buffersproxy_buffer_size.

Un ejemplo que aumenta el número de búferes de proxy disponibles para cada solicitud ascendente, mientras que recortar el búfer que probablemente almacena los encabezados se vería así:

Por el contrario, si tienes clientes rápidos a los que deseas mostrar inmediatamente los datos, puedes desactivar el búfer por completo. Nginx aún utilizará buffers si el flujo ascendente es más rápido que el cliente, pero intentará descargar los datos al cliente inmediatamente en lugar de esperar a que el buffer se agrupe. Si el cliente es lento, esto puede hacer que la conexión ascendente permanezca abierta hasta que el cliente pueda ponerse al día. Cuando el búfer está “off”, solo se utilizará el búfer proxy_buffer_size definido por la directiva:

Alta disponibilidad (opcional)

El proxy Nginx se puede hacer más robusto agregando un conjunto redundante de balanceadores de carga, creando una infraestructura de alta disponibilidad.

Una configuración de alta disponibilidad (high availability – HA) es una infraestructura sin un solo punto de falla, y sus balanceadores de carga son parte de esta configuración. Al tener más de un balanceador de carga, previenes el posible tiempo de inactividad si tu balanceador de carga no está disponible o si necesita retirarlos para su mantenimiento.

Aquí hay un diagrama de una configuración básica de alta disponibilidad:

En este ejemplo, tienes varios balanceadores de carga (uno activo y uno o más pasivos) detrás de una dirección IP estática que se puede volver a asignar de un servidor a otro. Las solicitudes de los clientes se enrutan desde la IP estática al balanceador de carga activo, y luego a los servidores de back-end.

Configuración del almacenamiento en caché de proxy para reducir los tiempos de respuesta

Si bien el almacenamiento en búfer puede ayudar a liberar el servidor backend para manejar más solicitudes, Nginx también proporciona una manera de almacenar en caché el contenido de los servidores backend, eliminando la necesidad de conectarse al flujo ascendente para muchas solicitudes.

Configurando un Proxy Cache

Para configurar una memoria caché para usar con contenido proxy, podemos usar la directiva proxy_cache_path. Esto creará un área donde se pueden mantener los datos devueltos desde los servidores proxy. La directiva proxy_cache_path debe establecerse en el contexto http.

En el siguiente ejemplo, configuraremos esta y algunas directivas relacionadas para configurar nuestro sistema de almacenamiento en caché.

Con la directiva proxy_cache_path, hemos definido un directorio en el sistema de archivos donde nos gustaría almacenar nuestro caché. En este ejemplo, hemos elegido el directorio /var/lib/nginx/cache. Si este directorio no existe, puedes crearlo con el permiso y la propiedad correctos escribiendo:

El parámetro levels= especifica cómo se organizará el caché. Nginx creará una clave de caché mediante la combinación del valor de una clave (configurada a continuación). Los niveles que seleccionamos anteriormente dictan que se creará un directorio de un solo carácter (este será el último carácter del valor de hash) con un subdirectorio de dos caracteres (tomado de los siguientes dos caracteres del final del valor de hash). Por lo general, no tendrás que preocuparte por los detalles de esto, pero ayuda a Nginx a encontrar rápidamente los valores relevantes.

El parámetro keys_zone= define el nombre para esta zona de caché, a la que hemos llamado backcache. Aquí también es donde definimos cuántos metadatos almacenar. En este caso, estamos almacenando 8 MB de claves. Para cada megabyte, Nginx puede almacenar alrededor de 8000 entradas. El parámetro max_size establece el tamaño máximo de los datos almacenados en caché reales.

Otra directiva que utilizamos anteriormente es proxy_cache_key. Esto se utiliza para establecer la clave que se utilizará para almacenar los valores almacenados en caché. Esta misma clave se usa para verificar si se puede atender una solicitud desde el caché. Estamos configurando esto para una combinación del esquema (http o https), el método de solicitud HTTP, así como el host solicitado y el URI.

La directiva proxy_cache_valid se puede especificar varias veces. Nos permite configurar cuánto tiempo almacenar los valores en función del código de estado. En nuestro ejemplo, almacenamos éxitos y redirecciones durante 10 minutos, y expiramos la memoria caché para respuestas 404 cada minuto.

Ahora, hemos configurado la zona de caché, pero aún tenemos que decirle a Nginx cuándo usar el caché.

En las ubicaciones en las que hacemos proxy a un backend, podemos configurar el uso de este caché:

Usando la directiva proxy_cache, podemos especificar que la zona backcache de caché debería usarse para este contexto. Nginx buscará aquí una entrada válida antes de pasar al backend.

La directiva proxy_cache_bypass se establece en la variable $http_cache_control. Esto contendrá un indicador de si el cliente está solicitando explícitamente una versión nueva y no en caché del recurso. Establecer esta directiva permite a Nginx manejar correctamente estos tipos de solicitudes de clientes. No se requiere ninguna otra configuración.

También agregamos un encabezado adicional llamado X-Proxy-Cache. Establecemos este encabezado al valor de la variable $upstream_cache_status. Básicamente, esto establece un encabezado que nos permite ver si la solicitud resultó en un acierto de caché, una falla de caché o si el caché se omitió explícitamente. Esto es especialmente valioso para la depuración, pero también es información útil para el cliente.

Notas sobre los resultados del almacenamiento en caché

El almacenamiento en caché puede mejorar enormemente el rendimiento de tu proxy. Sin embargo, definitivamente hay consideraciones que deben tenerse en cuenta al configurar la memoria caché.

En primer lugar, los datos relacionados con el usuario no deben almacenarse en caché. Esto podría resultar en que los datos de un usuario se presenten a otro usuario. Si tu sitio es completamente estático, esto probablemente no sea un problema.

Si tu sitio tiene algunos elementos dinámicos, tendrás que tener esto en cuenta en los servidores backend. La forma en que se maneje esto depende de qué aplicación o servidor esté manejando el procesamiento del servidor. Para el contenido privado, debe establecer el encabezado Cache-Control en “no-cache”, “no-store” o “private” dependiendo de la naturaleza de los datos:

  • no-cache:

indica que la respuesta no debe volver a mostrarse sin verificar primero que los datos no hayan cambiado en el servidor. Esto se puede utilizar si los datos son dinámicos e importantes. Se verifica un encabezado de metadatos hash ETag en cada solicitud y se puede mostrar el valor anterior si el backend devuelve el mismo valor hash.

  • no-store:

nos indica que en ningún momento los datos recibidos se almacenarán en caché. Esta es la opción más segura para datos privados, ya que significa que los datos se deben recuperar del servidor cada vez.

  • private:

esto muestra que ningún espacio de caché compartido debe almacenar estos datos. Esto puede ser útil para indicar que el navegador de un usuario puede almacenar en caché los datos, pero el servidor proxy no debe considerar estos datos como válidos para solicitudes posteriores.

  • public:

esto indica que la respuesta son datos públicos que se pueden almacenar en caché en cualquier punto de la conexión.

Un encabezado relacionado que puede controlar este comportamiento es el encabezado max-age, que indica la cantidad de segundos que se debe almacenar en caché cualquier recurso.

La configuración correcta de estos encabezados, según la sensibilidad del contenido, te ayudará a aprovechar la memoria caché a la vez que mantienes seguros tus datos privados y tus datos dinámicos actualizados.

Si tu backend también usa Nginx, puedes configurar algo de esto usando la directiva expires, que establecerá el max-age para Cache-Control:

En el ejemplo anterior, el primer bloque permite que el contenido se almacene en caché durante una hora. El segundo bloque establece el encabezado Cache-Control en “no-cache”. Para establecer otros valores, puedes utilizar la directiva add_header, de esta manera:

Conclusión

Nginx es ante todo un proxy inverso, que también tiene la capacidad de funcionar como un servidor web. Debido a esta decisión de diseño, enviar solicitudes a otros servidores es bastante sencillo. Sin embargo, Nginx es muy flexible, ya que permite un control más complejo sobre tu configuración de proxy si lo deseas.