Configurar vhosts en nginx

Como ya comenté en el pasado, para este sitio (y otros en el mismo server) estoy utilizando Apache para servir el PHP en el puerto 8080 y al frente un nginx como reverse proxy, que redirecciona las peticiones PHP al Apache y sirve los archivos estáticos, utilizando el prótocolo http2.

Entonces vamos a ver como configurar los vhosts en ambos servers para poder servir más de un sitio desde la misma VM.

Configurar Apache

Para agregar vhosts a Apache vamos a ir a /etc/apache2/sites-available y agregar ahi las configuraciones. En este ejemplo vamos a suponer que agregamos sitio1.com y sitio2.com, cuyos dominios ya deberían estar apuntando a los nameservers de nuestro hosting.

Entonces vamos a crear el archivo sitio1.conf:

<VirtualHost *:8080>
      ServerAdmin [email protected]
      ServerName  sitio1.com
#esta linea NO HACE FALTA, ya que nginx se encargará de redireccionar www, la dejamos a modo de ejemplo
      ServerAlias www.sitio1.com 
      DocumentRoot /var/www/vhosts/sitio1/htdocs
        <Directory /var/www/vhosts/sitio1/htdocs>
                Options FollowSymLinks
                AllowOverride All
                Require all granted
        </Directory>
      AccessFileName .htaccess
      LogLevel warn
      ErrorLog  /var/www/vhosts/sitio1/logs/error.log
      CustomLog /var/www/vhosts/sitio1/logs/access.log combined
</VirtualHost>

Y sitio2.conf:

<VirtualHost *:8080>
      ServerAdmin [email protected]
      ServerName  sitio2.com
      DocumentRoot /var/www/vhosts/sitio2/htdocs
        <Directory /var/www/vhosts/sitio2/htdocs>
                Options FollowSymLinks
                AllowOverride All
                Require all granted
        </Directory>
      AccessFileName .htaccess
      LogLevel warn
      ErrorLog  /var/www/vhosts/sitio2/logs/error.log
      CustomLog /var/www/vhosts/sitio2/logs/access.log combined
</VirtualHost>

Observando el archivo se ve que sitio1 se sirve desde /var/www/vhosts/sitio1/htdocs y sitio2 desde /var/www/vhosts/sitio2/htdocs. También tengo la costumbre de separar los logs por vhost, de esta forma es más simple encontrar errores o tomar métricas específicas para cada sitio. Por esto vamos a crear los directorios que necesitamos:

mkdir -p /var/www/vhosts/sitio1/htdocs
mkdir /var/www/vhosts/sitio1/logs
mkdir -p /var/www/vhosts/sitio2/htdocs
mkdir /var/www/vhosts/sitio2/logs

Como último paso, nos queda habilitar las configs, para esto hacemos:

a2ensite sitio1
a2ensite sitio2
service apache2 restart

Con esto terminamos la configuración de apache, pero todavía no podemos acceder a nuestros sitios porque, recordemos, el que sirve desde el puerto 443 es nginx, asi que vamos al siguiente paso.

Configurar nginx

La configuración de nginx es muy similar a la de Apache, de hecho usan la misma estructura de directorios (solo que los archivos de configuración de nginx, al igual que los de apache anteriores a la versión 2.4, no tienen extensión). Asi que nos vamos al directorio /etc/nginx/sites-available y creamos nuestros dos archivos, sitio1 y sitio2:

#archivo /etc/nginx/sites-available/sitio1
server {
    listen 443 deferred ssl http2 default_server;
    listen [::]:443 deferred ssl http2 default_server;

    # gzip should not be used with ssl
    gzip off;

    ssl_certificate /etc/letsencrypt/live/sitio1.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sitio1.com/privkey.pem;

    root /var/www/vhosts/sitio1/htdocs;
    index index.php;

    server_name sitio1.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 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate /etc/letsencrypt/live/sitio1.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sitio1.com/privkey.pem;

    server_name www.sitio1.com;
    return         301 https://sitio1.com$request_uri;
}

server {
    listen         80;
    listen    [::]:80;
    server_name    sitio1.com www.sitio1.com;
    return         301 https://$server_name$request_uri;
}
#archivo /etc/nginx/sites-available/sitio2
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    # gzip should not be used with ssl
    gzip off;

    ssl_certificate /etc/letsencrypt/live/sitio2.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sitio2.com/privkey.pem;

    root /var/www/vhosts/sitio2/htdocs;
    index index.php;

    server_name sitio2.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 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate /etc/letsencrypt/live/sitio2.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sitio2.com/privkey.pem;

    server_name www.sitio2.com;
    return         301 https://sitio1.com$request_uri;
}

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

Básicamente acá lo que hacemos es: escuchar en el puerto 443 y servir contenido de sitio1.com y sitio2.com, en el mismo puerto redireccionar www.sitio1.com y www.sitio2.com a sus correspondientes URLs sin el www y escuchar el puerto 80 para redireccionar a https. Recordemos que esta configuración se complementa con la configuración de SSL de nginx.

Para que nginx efectivamente lea las configuraciones tenemos que crear los links simbólicos desde sites-enabled:

cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/sitio1 sitio1
ln -s /etc/nginx/sites-available/sitio2 sitio2

Esto es exactamente lo mismo que hace apache automáticamente si usamos a2ensite.

Validamos que la config esté OK y reiniciamos Nginx:

nginx -t
service nginx restart

Y con esto estamos listos para servir nuestros dos sitios desde el mismo server.

Ya se pueden agregar los archivos a las carpetas /var/www/vhosts/sitio1/htdocs y /var/www/vhosts/sitio2/htdocs y ver el contenido de ambos sitios. Igual más que subir via ftp o scp yo prefiero utilizar git para hacer los despliegues.

Espero les sea útil.