systemadmin.es > LAMP y web > Instalación de nginx en modo proxy con un Apache con VirtualHosts de backend

Instalación de nginx en modo proxy con un Apache con VirtualHosts de backend

Anteriormente hemos visto como instalar nginx como proxy, pero solo nos va a servir para backends que no necesiten del header Host para funcionar. Un ejemplo muy común sería el caso de Apache con VirtualHosts.

nginx NOW!

nginx NOW!

Para indentificar a que VirtualHost debe ir una petición HTTP/1.1 se usa el headerHost“:

GET / HTTP/1.1
Referer: http://www.bmbupc.org/
Accept-Language: zh-cn,zh-hk,zh-tw,en-us
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Accept: */*
Host: systemadmin.es
Connection: Keep-Alive
Range: bytes=0--1177288705
Accept-Encoding: gzip

En el RFC2616 podemos encontrar la definición de HTTP/1.1 y en la sección 14.23 la definición del header Host que hace posibles los VirtualHosts, o lo que es lo mismo, disponer de más de un sitio web un una misma IP y puerto.

Para servir los VirtualHosts del Apache a través de un nginx deberemos modificar la configuración de ambos servidores web. Vamos a ver primero nginx.

Deberemos añadir a la configuración inicial de nginx como proxy los siguientes parámetros:

        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

Estas lineas añaden tres headers a la petición que realiza el nginx al apache:

Host: systemadmin.es
X-Real-IP: 84.73.199.229
X-Forwarded-For: 84.73.199.229

Las funciones de estos headers nuevos son:

  • Host: Tal como deciamos anteriormente, para identificar el VirtualHost
  • X-Real-IP: Indicamos la IP con la cual se esta conectando el cliente al nginx
  • X-Forwarded-For: Indicamos la IP del cliente y los proxys a través de los cuales nos ha llegado la petición

La configuración del nginx quedaría:

user  nginx;
worker_processes  4;

daemon off;

events
{
    worker_connections  1024;
}

http
{
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  3;
    gzip  on;

    server
    {
        listen       80;
        server_name    _;

        access_log      off;

        location / {
            proxy_pass         http://127.0.0.1:8080;
        }

        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

A continuación si queremos usar los logs de apache para generar estadísticas deberemos usar el mod_rpaf para indicar al apache que use el X-Forwarded-For o el X-Real-IP como IP de origen en el fichero de log.

cd /usr/local/src
wget http://stderr.net/apache/rpaf/download/mod_rpaf-0.6.tar.gz
tar zxvf mod_rpaf-0.6.tar.gz
cd mod_rpaf-0.6

Deberemos modificar el Makefile para que encuentre el apxs, ya que por defecto lo busca con el nombre “apxs2“:

sed -i 's/APXS2=\$(shell which \(apxs2\))/APXS2=\$(shell which apxs)/' Makefile

También deberemos, si no esta ya, el bin del apache al PATH:

export PATH=$PATH:/usr/local/apache22/bin/

Finalmente compilamos e instalamos con los siguientes comandos:

make rpaf-2.0 && make install-2.0

Finalmente deberemos añadir a la configuración del apache el módulo y lo configuramos:

echo "LoadModule rpaf_module modules/mod_rpaf-2.0.so" >> /usr/local/apache22/conf/httpd.conf
echo "RPAFenable On" >> /usr/local/apache22/conf/httpd.conf
echo "RPAFsethostname On" >> /usr/local/apache22/conf/httpd.conf
echo "RPAFproxy_ips 127.0.0.1" >> /usr/local/apache22/conf/httpd.conf
echo "RPAFheader X-Forwarded-For" >> /usr/local/apache22/conf/httpd.conf

Con el parámetro RPAFheader indicamos que header queremos usar para identificar el cliente, podemos usar tanto X-Forwarded-For como X-Real-IP. En este caso usaremos X-Forwarded-For.

A continuación deberemos modificar la configuración del Apache para cambiar el puerto del 80 al, por ejemplo, 8080:

sed -i -e 's/VirtualHost\(\s*[^:]*\):80>/VirtualHost\1:8080>/g' -e 's/NameVirtualHost\(\s*[^:]*\):80\s*$/NameVirtualHost\1:8080/g' /usr/local/apache22/conf/extra/httpd-vhosts.conf
for i in $(cat /usr/local/apache22/conf/extra/httpd-vhosts.conf  | grep Include | awk '{ print $NF }'); do for a in $(ls /usr/local/apache22/$i); do sed -i -e 's/VirtualHost\(\s*[^:]*\):80>/VirtualHost\1:8080>/g' -e 's/NameVirtualHost\(\s*[^:]*\):80\s*$/NameVirtualHost\1:8080/g' $a; done; done
sed 's/Listen\(\s\)80\s*$/Listen\18080/' -i /usr/local/apache22/conf/httpd.conf

Deberemos reiniciar tanto el apache como el nginx para aplicar los cambios:

svc -t /service/apache22/
svc -t /service/nginx/

Con esto ya tendremos los logs como si no hubiera el nginx en frente del Apache.

12 comments to “Instalación de nginx en modo proxy con un Apache con VirtualHosts de backend”

  1. Me gustaría hacerte una corrección: Muy lindo todo lo que pusiste y es de gran utilidad. Pero debo comentarte, estando MUY MOLESTO, que la implementación para GNU (al menos en gentoo-based) de nginx no necesita escapar los $ con \, poruqe los traduce literalmente y rompe el header http. Estuve debuggeando casi una hora hasta entender que el problema era esto.

    Gracias, me salvaste. Pero si moderás los comentarios, al menos agregá esa corrección.

  2. Hola Pablo,
    La configuración de nginx esta metida con backslash en $ porque esta dentro de un cat con heredocument:

    cat > /tmp/heredocument <<EOF
    $USER  \$USER
    EOF
    

    Si luego haces el cat veras porque se debe escapar el $

    # cat /tmp/heredocument
    root  $USER
    

    Voy a quitar en el post el heredocument de la configuración de nginx porque puede inducir a error.

    Muchas gracias por tu comentario!

    saludos,

  3. Hola Jordi

    Muy interesante tu artículo. También he creído conveniente, reforzar la “seguridad” de Apache 1.x/2.X mediante Nginx como proxy. En tu otro post, sobre slowris, te comente acerca de mod_qos. Estoy de acuerdo en que, cuando el problema es de fondo, los parches son una solución dudosa.

    Simplemente te confirmo que funciona a la perfección. Por otro lado, me ha surgido una duda al implementar Nginx como proxy sobre otro webserver. Es bastante conocido la diferencia de rendimiento entre Nginx y Apache. Poniendo Nginx como proxy, además de seguridad, intuyo que algo de rendimiento ganamos. En este sentido, ¿disponemos de alguna forma, quizá cache, para aumentar de paso el rendimiento del site?

    Saludos 🙂

  4. En realidad todo lo contrario, añades latencia a la conexión: Para una petición de un cliente primero establece la conexión con el nginx, luego el nginx establece una nueva conexión con el backend apache, se transmiten los datos y se cierra la conexión entre el nginx y el backend.

    Por lo tanto, tienes almenos el delay del handshake TCP *2 (abrir cliente-nginx y abrir nginx-apache)

    Esto es así porque el proxy de nginx es HTTP/1.0, por lo que quedan conexiones persistentes entre nginx y Apache porque el keepalive se define en HTTP/1.1

    Por lo tanto con esta configuración no ganas rendimiento, sino que lo pierdes (ganando seguridad). A partir de esta configuración puedes reducir el consumo de recursos del sistema sirviendo el contenido estático directamente con el nginx

  5. Gracias por la aclaración. El estático ya lo sirvo desde Nginx :). De todas formas, entre añadir latencia a la conexión y tener una caída de los servidores cada cierto tiempo, prefiero lo primero. Creo que me toca ponerme con Nginx + PHP FastCGI y hacer algunas pruebas 😛

    Un saludo

  6. Hola, buen articulo pero no entiendo eso de añadir seguridad al apache, en que me beneficia ?

    Como dices se pierde un poco de velocidad.

    No es mejor usar nginx + php y olvidarse de apache ?

    Al menos yo tengo algún servidor corriendo solo bajo NGINX, NGINX + PHP, luego lo deje, mas que nada por que no sabia bien usar el htaccess de apache y sus normas ( pretty urls de wordpress, algunas cosas de smf y otros cms ) ahora uso NGINX en otro servidor para servir solo el contenido estático desde el.

    Ahora bien, la pregunta es, como puedo hacer para tener apache funcionando para X webs, sería apache + PHP, y en el mismo servidor tener NGINX para otra web que solo sirve contenido estático ( imágenes, vídeos etc )

    Lo digo por que un día intente correr NGINX en otro puerto y la web no me cargaba, osea es obligado tener NGINX en el puerto 80 y que este sirva como proxy de apache que esta en otro puerto ?

    No se puede tener apache en el 80 funcionando con normalidad, sirviendo unas 30 o 40 webs y NGINX por otro lado solo para una web ?

  7. No puedes tener dos daemons escuchando en el mismo puerto, deberías usar otra IP

    saludos,

  8. Si eso ya lo se pero lo que digo es tener dos servidores web en este caso nginx y apache, en puertos diferentes, apache sirviendo web dinamicas dominio1.com y nginx sirviendo imágenes, vídeos y archivos comprimidos dominio2.com

    Lo que pasa es que no puedo hacer correr nginx en otro puerto.

    Aunque bueno, tampoco había pensado en contratar otra ip, veré que opciones tengo y veré si puedo resolver lo de nginx y si no pues usare 2 ips.

    Gracias por contestar

  9. En el caso que dominio1.com tenga la misma IP que dominio2.com no puedes porque no pueden escuchar dos daemons en la misma IP y puerto.

    Si son dos IPs diferentes no hay problema, es cambiar el Listen de *:80 a IP:80 y lo mismo para nginx.

    Puedes hacer escuchar el nginx en otro puerto (web:81) pero te puede dar problemas en muchos firewalls, yo no lo haría.. Simplemente es decirle en el server listen 81:

    server
    {
    listen 81;
    (...)
    }
    
  10. Gracias por contestar jordi, eso de cambiar el puerto ya lo he intentado y si nginx inicia con normalidad en otro puerto, pero si tengo nginx y apache y dejo corriendo nginx en el puerto 80 he intento correr apache en algún otro puerto como el 8080 apache no me inicia.

    Pero en fin pense que había solución a esto pero ahora que dice algunos firewalls, eso no lo había tomado en cuenta.

    Así que lo que haré será usar dos ips, o bien migrar todo a nginx + php, aunque tenga que aprender otra vez como van las directivas de nginx ( la alternativa a htaccess )

    En fin, los felicito por el blog, tiene muchísima información bastante útil.

    Gracias por las respuestas !

  11. Otra vez yo estoy por aquí, resulta que hemos implementado esto en un servidor pero nos hemos topado con un pequeño problema, esto funciona bien con wordpress, smf etc, pero no funciona con algunos otros cms.

    Resulta que hay URLS que tienen tildes y acentos ( en mybb ) y al dejar NGINX como proxy esas URLS se vuelven inaccesibles, al menos en firefox muestra este error.

    Firefox ha detectado que el servidor está redirigiendo la solicitud a esta dirección de una manera en la que nunca terminará.

    En fin ese mensaje muestra cuando se intenta acceder a una URL con algún carácter extraño, ñs, acentos etc…

    No sabes por que puede pasar esto ?

    Y como te digo funciona bien en todo lado menos en algunas URLS de ese cms, con WP y SMF no pasa nada.

    Un saludo 🙂

  12. Ah por cierto usamos el mod NGINX para cpanel bajo centos.

Deja un comentario:

XHTML - Tags permitidos:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>