Nginx: lo básico. Analizar nginx.conf.sample de Magento

Como desarrollador, siempre he tenido algo pendiente con Nginx… y con Apache también, aunque me ha quedado claro la superioridad y versatilidad de Nginx. Con la configuración de un servidor web en general.

Por eso quise probar de configurar un servidor que no sea en mi ordenador con el típico localhost y todo ya por defecto abierto y preparado para que todo funcione correctamente… ponerle su dominio, instalar Nginx desde cero, etc (en esta pequeña serie).

Aquí simplemente quiero volcar lo que he leído sobre sintaxis de Nginx y ver si puedo abrir el nginx.conf.sample de Magento, entenderlo todo bien y dejarlo a modo de apuntes. Porque me da mucha rabia hacer las cosas como un mono sin entender, y en este ficherito siempre he hecho las cosas sin mirarlo en detenimiento y con lo que he trasteado mi VPS creo que podría tenerlo todo bastante más claro.

Ficheros de configuración

Lo primer de todo es decir que hay varios

  • /etc/nginx/nginx.conf: es el principal fichero de configuración
    • tiene los bloques http y events
    • es quien hace los includes de los otros ficheros de configuración
      • include /etc/nginx/conf.d/*.conf
      • include /etc/nginx/sites-enabled/*
  • /etc/nginx/sites-enabled/*: los virtual hosts. Aquí normalmente hacemos enlaces simbólicos a ficheros ubicados realmente en /etc/nginx/sites-available/
    • tiene el bloque server
  • /etc/nginx/conf.d/*.conf : configuraciones adicionales

Contextos

Estos ficheros tienen contextos definidos por los bloques.

Los bloques son definidos por las llaves {} y si ay una directiva no englobada en ningún bloque, es el main context.

Los bloques http y events están en el main context.

El resto de bloques como server, location, etc, están dentro de un contexto (en el caso de server está dentro del contexto de http).

Location

Veremos muchos bloques location. Lo que hace es definir una configuración (lo que contenga el bloque) basado en una URI.

server {
    location / {
        root /data/www;
    }

    location /images/ {
        root /data;
    }
}

De esta forma estamos definiendo un sever con 2 location, donde simplemente distinguimos el document root de cada petición

  • Si la URI no tiene nada (es simplemente /…), usará como document root /data/www
  • Si la URI tiene images (es /images…), usará como document root /data

Puede ser definido como tal, o como expresión regular. Esto suele hacerse para las imágenes:

...
    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
...

Proxy

Es común utilizar Nginx como un Proxy Server. Redirige las peticiones de un servidor a otro. Se utiliza la directiva proxy_pass:

server {
    location / {
        proxy_pass http://localhost:8080/;
    }

    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
}

De esta forma estamos redirigiendo el tráfico que le llegue a la raíz del dominio (location /) y pasamos la petición a la url http://localhost:8080. Más abajo definimos que las imágenes serán servidas desde el document root /data/images

FastCGI

CGI: common gateway interface.

Lo que hace Nginx es un proxy a un servidor FastCGI. Esto será un proceso, un servicio, un programa, que recibirá las peticiones que originalmente le han llegado a Nginx (una especie proxy).

El ejemplo más conocido quizás es phpfmp (el servicio FastCGI), mediante la directiva fastcgi_pass cuya sintaxis es muy parecida a la de proxy_pass:

...
location ~ \.php$ {
    include snippets/fastcgi-php.conf;

    # With php-fpm (or other unix sockets):
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
...

Esta es la configuración que nos encontramos en el virtual host por defecto de Nginx (/etc/nginx/sites-available/default), para descomentar porque nos viene comentada, para pasar las peticiones al FastCGI de PHP. Lo que hace es que toda petición de un fichero .php la pasa al fichero socket de php.

Procesado de una petición

La distinción se hace desde el nombre de los virtual hosts y/o puerto.

Cuando a Nginx le llega una petición, coge el nombre del dominio y el puerto y va en busca una coincidencia en los bloques server:

server {
    listen      80;
    server_name example.org www.example.org;
    ...
}

server {
    listen      80;
    server_name example.net www.example.net;
    ...
}

server {
    listen      80;
    server_name example.com www.example.com;
    ...
}

Arriba, preparamos 3 virtual hosts diferentes distinguidos por el dominio mediante el server_name. Los 3 escuchan en el puerto 80 mediante listen.

El servidor por defecto será siempre el primero a menos que coloquemos al lado del puerto default_server para declarar ese servidor explícitamente como el de por defecto (solo puede haber 1).

Estando dentro de un bloque server Nginx mirará el bloque location y lo comparará con la URI, eligiendo primero el location más largo (más específico). Si el location tiene una expresión regular, Nginx las comparará en el orden de definición parando en la primera.

HTTPS

En un bloque server, hemos de configurar:

  • ssl_certificate: ruta a la key pública
  • ssl_certificate_key: ruta a la key privada
  • ssl_protocols: protocolos aceptados (suele estar en el bloque http)
  • ssl_ciphers: podemos usarlo para limitar qué cifrados usar

Configuraciones o directivas comunes

  • fastcgi_pass: proxy a un servicio FastCGI
  • listen: define el puerto
  • location: define un bloqe para una URI concreta para la cual aplicará la configuración que contenga
  • proxy_pass: define un proxy (pasar peticiones a otro servidor)
  • root: define el directorio que se usará para procesar el petición
  • server_name: define el nombre de un virtual host (dominios)
    • podemos ver como valor el guión bajo “_”: cuando no coincide el dominio con ningún server_name configurado, se usará este virtual host

nginx.confg.sample de Magento

Magento tiene ya por defecto un fichero nginx.conf.sample de ejemplo con lo básico y lo recomendado como mínimo para un servidor web Nginx. No estoy muy seguro de lo que voy a poner aquí… lo siguiente es aventurarme a configurar un servidor para magento y hacer que magento realmente se instale y tire bie… haré mi mejo intento de interpretación sin haberlo probado, si me lío con algo más adelante lo corregiré. Tampoco comentaré todo, solo lo más importante.

Spoiler: hay muchas cosas que no me quedan claras, siendo sincero…

Cosas que se hace en este fichero:

  • Nos pone en ## Example configuration lo que entiendo que va en el virtual host que deberemos copiar (solo esta parte) en nuestro fichero de configuración del virtual host… (/etc/nginx/sites-available/nombredominio.com)
    • define un upstream (grupo de servers para ser referenciados por otros módulos de Nginx)
      • en este caso el upstream fastcgi_backend donde define por defecto la conexión a phpfpm a través del fichero de socket
    • define un server, con:
      • puerto 80,
      • server_name (pondremos el nombre del dominio, por defecto pone mage.dev)
      • define la variable $MAGE_ROOT a un path donde está la aplicacion de Magento
      • un include a este fichero nginx.conf.sample en la propia instalación de nuestro Magento (tiene sentido)
  • Definiciones opcionales que recomienda hacer desde la línea de comandos con php bin/magento
  • Nos pide que carguemos un módulo de Nginx, ngx_http_image_filter_module que habremos de hacerlo en el nginx.conf al principio
  • Y todo el resto del fichero, realmente será incluido en include que hemos hecho a este mismo fichero desde el fichero de configuración del virtual host, por tanto en las siguientes líneas estamos configurando el virtual host en sí mismo:
    • define el root en $MAGE_ROOT/pub y el listado de archivos para acceder
    • deniega el acceso a los .user.ini
    • define un punto de acceso para la instalacion en la url …/setup
      • pasa los datos al socket de phpfpm con el upstream usando fastcgi_pass (especie de proxy)
      • unos cuantos parámetros con fastcgi_param
      • deniega el acceso a pub dentro de setup
    • define lo mismo para el update en ../update
      • más de lo mismo
    • el location / nos asegura que si no hay un index.php en la url que pruebe esos ficheros
    • el location /pub/ tiene otro location para denegar acceso a subdirectorios, y un alias para impedir acceso directo a pub/media/
    • el location /static/
      • nos pide descomentar una línea de expires para servidores de producción
      • quita de las url con un rewrite la firma para puentear la cache de navegador
      • hace otro location para las imágenes y el mismo rewrite
      • hace otro location para fichero comprimidos y xml, y el mismo rewrite
    • el location /media/
      • probar diferentes formas de la url
      • denegar acceso a los xml del subdir theme_customization
      • un location para extensiones de imagenes, tipografías, y ficheros de texto (js, css) para que sean accesibles
      • un location similar para los ficheros comprimidos y xml
    • varias denegaciones de subdirectorios de media y los xml de /errors/
    • el location principal, donde entra la aplicación PHP
      • con una expresion regular permite la entrada por varios nombres de fichero
      • pasa varios parámetros a PHP con fastcgi_param
    • habilita gzip y lo configura
    • denegar acceso a otros .php, .phtml, .htaccess, .git