Usar nginx como reverse proxy (Y utilizar http 2)

Si sos curioso, habrás notado que este blog está usando el protocolo http2 para servir las páginas:

Para esto utilicé una configuración que es bastante popular: Nginx como reverse proxy comunicándose con el cliente y sirviendo el contenido estático, y delegando en Apache (que escucha en otro puerto) el contenido PHP.

Vamos a ver el paso a paso para dejar este stack andando en Ubuntu 16.04.

Instalar Apache, Nginx y PHP7

Como primer y obvio paso, instalamos el stack (para este artículo voy a obviar la instalación de la DB, yo personalmente suelo utilizar mariaDB por sobre MySQL)

Instalamos apache:

sudo apt install apache2

Ya en este momento, si accedemos al server por su IP deberíamos ver la página default de apache en ubuntu:

Como próximo paso, instalamos PHP:

sudo apt install php libapache2-mod-php

Acá solo instalé lo básico, seguramente se van a necesitar varias librerías más (curl, xml, db, etc). Vamos a chequear que PHP esté instalado, creando un archivo info en /var/www/html/info.php con el siguiente contenido:

<?php
phpinfo();

Ahora, yendo a http://ip_publica/info.php deberíamos ver algo así:

Perfecto. Tenemos PHP7 corriendo sobre apache, ahora vamos a instalar nginx como reverse proxy. Para eso tenemos que detener apache (sino la instalación de nginx nos va a dar errores, porque ambos van a querer escuchar por el mismo puerto, el 80). Entonces corremos los siguientes comandos en nuestro server:

sudo apache2ctl stop
sudo apt install nginx

Si ahora nos vamos nuevamente a http://ip_publica, debería ser Nginx el que nos reciba:

Tenemos todo instalado, vamos a configurar un par de cosas.

Modificar los puertos de apache

El que va a servir el contenido al cliente (por los puertos 80 y 443) va a ser nginx, por lo que vamos a modificar apache para que deje esos puertos libres y escuche, por ejemplo, en el 8080 y el 8043. Modificamos entonces el archivo /etc/apache2/ports.conf para que quede así:

Listen 8080

<IfModule ssl_module>
        Listen 8043
</IfModule>

<IfModule mod_gnutls.c>
        Listen 8043
</IfModule>

Una vez hecho esto, ya podemos habilitar Apache nuevamente, ya que no hay más conflicto con Nginx:

sudo apache2ctl start

(Test rápido: si vamos a http://ip_publica:8080 deberíamos ver la misma página default de Apache que en el 1er paso).

Obtener certificado SSL

Un detalle no menor: si bien en el prótocolo HTTP/2 el uso de cifrado TLS es opcional, los browsers más importantes ya han anunciado que solo soportarán HTTP 2.0 sobre TLS, por lo que vamos a necesitar un certificado para nuestro sitio. Por suerte la gente de Let's Encrypt está haciendo un gran trabajo y obtener un certificado gratuito es hoy bastante sencillo.

Configurar nginx como reverse proxy

Con Apache escuchando en el puerto 8080 y nuestro certificado SSL instalado, es hora de configurar nginx. Vamos a modificar el archivo /etc/nginx/sites-available/default (se puede crear un nuevo archivo en esa carpeta, especialmente si la idea es utilizar vhosts, pero por simplicidad modificaremos el sitio default):

server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;

    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    root /var/www/html;
    index index.php index.html index.htm;

    server_name example.com;

    location / {
        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:8080;
    }
    location ~* \.(gif|jpg|jpeg|js|css)$ {
        try_files $uri =404;
    }
    location ~ /\.ht {
        deny all;
    }
}

server {
    listen         80;
    listen    [::]:80;
    server_name    example.com 
    return         301 https://$server_name$request_uri;
}

Esta configuración hace básicamente lo siguiente:

Escucha en el puerto 443 utilizando el protocolo HTTP/2, y pasa todo a apache (al puerto 8080), excepto el contenido estático (imágenes, js, css). También escucha en el puerto 80 y redirecciona a https. Nota: si se quiere una capa más de seguridad, se puede enviar el contenido al puerto 8043 utilizando TLS, lo cual tendra un pequeño costo en performance.

Y eso es todo. Reiniciamos nginx y tenemos configurado nuestro server con un reverse proxy y sirviendo contenido con HTTP/2.

Hay muchos factores a tener en cuenta que dejé de lado en este artículo (modificar ssl_protocols y ssl_ciphers de Nginx, configurar virtual hosts, modificar los keep alive de ambos servers, etcétera) porque la idea era mantenerlo simple y que sirva como punto de partida para quien quiera utilizar esta configuración que cada vez gana más popularidad.