Configurare NGINX

Ho già avuto modo di parlare di NGINX in un articolo precedente, nel quale ne ho descritto l’installazione del software da file sorgenti. In questo post invece voglio provare a descrivere in che modo si configura il server NGINX al fine di servire uno o più siti web.

Contesti e direttive

Per iniziare, bisogna tenere presente che un file di configurazione di NGINX è suddiviso in contesti (o sezioni), che fungono da contenitori per le direttive. Le direttive sono delle opzioni per il server espresse sotto forma di nome e valore. I contesti possono essere innestati tra loro e, in alcuni casi, ripetuti più volte. Ciascun contesto ha un suo nome; le direttive definite all’interno di un contesto hanno valore per quel contesto e per quelli eventualmente in esso contenuti (contesti innestati), a meno che la stessa direttiva non sia ridefinita in un contesto più interno. Vediamo un esempio tratto dal file di configurazione di default su una macchina Ubuntu Server 20.04:

# main context
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}
http { # http context
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    keepalive_timeout  65;

    server { # virtual host context
    listen       80;
    server_name  www.example.com;

    access_log  /var/log/nginx/example.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
    }
}

In questo esempio di configurazione si osserva il contesto principale, contenente il contesto http che a sua volta contiene uno (o più) contesti server. Nel contesto http è definita la direttiva access_log, che vale anche per tutti i contesti interni a meno che non venga ridefinita esplicitamente, come effettivamente accade all’interno del contesto server.

Come è facile intuire, ci sono direttive che possono essere di carattere generale (direttive globali) e direttive che invece vanno definite all’interno di contesti specifici.

Virtual hosts

Sebbene NGINX possa funzionare anche come email proxy, la sua funzione principale è quella di server web, ed è in questa modalità che ne mostrerò la configurazione.

Finalità di un server web è quella di rispondere alle richieste dei client, tipicamente browser web, inviando contenuti e pagine web tramite il protocollo http. Una singola pagina web può contenere generalmente testo semplice in sintassi html, direttive che ne definiscono lo stile di visualizzazione e/o link ai corrispondenti file .css, immagini e codice JavaScript (o link ai corrispondenti file .js). Un file di configurazione minimale di NGINX prevede necessariamente la sezione events (eventualmente vuota) e la sezione http, all’interno della quale definiamo una o più sezioni server. Le sezioni server rappresentano in questo caso i cosiddetti virtual hosts, ovvero i siti web che vogliamo servire tramite il nostro web server.

events {}

http {
  # Primo virtual host (sito)
  server {
    listen 80; # porta su cui il server è in ascolto;
               # se omessa, si assume sempre 80
    server_name example.com; # nome host (o indirizzo ip)
                             # associato a questo sito
    root /srv/example.com/public_html; # directory nella
              # quale sono posti i file che costituiscono il sito web
    
  } # fine primo virtual host
} # fine contesto http

Come detto in precedenza, però, una pagina web può fare riferimento ad altre risorse come file JavaScript o file CSS, e per queste tipologie di file va specificato in che modo interpretarli. A tal fine, nel blocco http dovremmo specificare il blocco types che istruisce il web server in tal senso, fornendo informazioni al client su come interpretare i dati ricevuti. Piuttosto che riportare in-line tutto il contenuto di un simile blocco (che può risultare piuttosto lungo), possiamo definire il blocco types all’interno di un file dedicato e quindi includere, tramite la direttiva include, tale file all’interno del file di configurazione principale. Il nostro file diverrebbe quindi simile al seguente…

events {}

http {

  include mime.types;  # direttiva per includere il file mime.types

  # Primo virtual host (sito)
  server {
    listen 80; # porta su cui il server è in ascolto;
               # se omessa, si assume sempre 80
    server_name example.com; # nome host (o indirizzo ip)
                             # associato a questo sito
    root /srv/example.com/public_html; # directory nella
              # quale sono posti i file che costituiscono il sito web
    
  } # fine primo virtual host
} # fine contesto http

…dove il file mime.types, presente nella stessa directory del file di configurazione principale, conterrà qualcosa come:

types {
    text/html                                        html htm shtml;
    text/css                                         css;
    text/xml                                         xml;
    image/gif                                        gif;
    image/jpeg                                       jpeg jpg;
    application/javascript                           js;
    application/atom+xml                             atom;
    application/rss+xml                              rss;

    text/mathml                                      mml;
    text/plain                                       txt;
    text/vnd.sun.j2me.app-descriptor                 jad;
    text/vnd.wap.wml                                 wml;
    text/x-component                                 htc;

    image/png                                        png;
    ....
    ....
}

Il file mime.types è generalmente incluso in un’installazione standard di nginx, sia che lo si installi da sorgenti che da pacchetti precompilati.

Locations

Abbiamo visto in che modo la direttiva root stabilisca la directory (del file system) a partire dalla quale il server web cerca le risorse che gli vengono richieste. Nell’esempio riportato sopra, dove root è impostato a /srv/example.com/public_html, nel caso in cui un browser richiedesse la pagina example.com/product/book.html, il web server andrebbe a cercare il file book.html nella directory /srv/example.com/public_html/product/. Questa modalità di funzionamento è più che sufficiente quando si ha soltanto necessità di erogare risorse statiche. Tuttavia NGINX permette di differenziare la sua risposta in base al “pattern” della richiesta ricevuta, grazie alla direttiva location.

Un esempio di direttiva location è presente già nel primo esempio di configurazione riportato nell’articolo, ma lo mostro nuovamente per maggiore chiarezza:

location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
}

In questo caso questa direttiva fa il match con qualunque richiesta inizi con il carattere /, quindi praticamente tutte le richieste, e al suo interno va poi a specificare la directory (root) dalla quale recuperare le risorse richieste nonché quali file (index) mostrare nel caso in cui nella richiesta non venga specificato alcun file specifico. Questo tipo di match è quello più generico ed è chiamato “prefix match”. Esistono altri tipi di match che è possibile usare con la direttiva location e sono, in ordine di priorità:

  • exact match (modificatore =)
  • preferential prefix match (modificatore ^~)
  • regex match (modificatore ~ oppure *~)

La sintassi generale per la direttiva location è quindi la seguente:

location <modificatore> <espressione> {
    # blocco della direttiva
}

Anche i blocchi location, salvo alcune eccezioni, possono essere innestati. Per quanto concerne la priorità, sulla documentazione di NGINX si legge quanto segue:

Per trovare il blocco location che corrisponde ad una data richiesta, nginx prima controlla le location definite usando i prefissi (prefix locations). Tra tutte queste, la location che ha il match più lungo (longest matching prefix) viene selezionata e memorizzata. Successivamente vengono controllate le location definite tramite espressioni regolari; il controllo è eseguito in base all’ordine in cui compaiono nel file di configurazione, e la ricerca si interrompe al primo match. Se vi è un match con una regex, allora viene usata la corrispondente location, altrimenti viene usata la prefix location memorizzata precedentemente.

Tradotto dalla documentazione di NGINX relativamente alla direttiva location.

E’ evidente che questa direttiva conferisce ad NGINX una notevole flessibilità e, usata opportunamente, permette a questo server web di servire anche contenuti generati dinamicamente (ad esempio tramite script php). Tuttavia non approfondisco ulteriormente questo aspetto in questo articolo.

Variabili, rewrite e redirect

L’utilizzo della direttiva location, combinata con le direttive di rewrite o redirect e con l’uso delle variabili, permette di gestire in maniera ancora più flessibile le richieste. Un caso tipico è quello in cui si rende necessario servire delle risorse che nel frattempo sono state spostate nel file system e che quindi non si possono recuperare sempicemente in base al path relativo alla root.

Anche in questo caso non approfondisco l’argomento, per il quale vi rimando alla documentazione ufficiale di NGINX. Tuttavia non escludo di scrivere in futuro un post dedicato.

WWW or not WWW?

Storicamente i siti web venivano configurati per rispondere al nome host “www” di un determinato dominio; per questo motivo ci siamo abituati un po’ tutti a digitare www.nomedelsito.tld (tld sta per top level domain) ogni volta che vogliamo accedere alle pagine web di un sito. Tuttavia questa è soltanto una convenzione scaturita dal modo con cui i sistemisti decidevano di pubblicare i loro siti web, convenzione derivata dall’inventore del web, Tim Berners Lee, che successivamente si è pentito di questa scelta arrivando ad affermare che, se potesse tornare indietro, probabilmente farebbe una scelta diversa.

Comunque sia, non sta scritto da nessuna parte che il nome di un sito debba cominciare con www, ed infatti molti siti non lo prevedono, essendo accessibili con il solo nome a domino (nomedominio.tld). Un esempio è il sito che state leggendo adesso, accessibile col nome verolinux.org senza il www iniziale. Siccome però molti conservano ancora l’abitudine di anteporre il www davanti al nome, è bene configurare il server web affinché tenga conto di questa eventualità comportandosi in due modi diversi (a seconda delle preferenze del proprietario del sito):

  1. mostrare correttamente il sito anche quando richiamato con il www
  2. reindirizzare le richieste contenenti il www portanto l’utente al sito senza il www

Entrambe le scelte sono corrette, ma io preferisco di gran lunga la seconda. Vediamo allora come possiamo configurare NGINX per fare in modo che un utente che richieda il sito www.example.com venga reindirizzato al sito example.com. Tenete presente che, volendo, i due nomi potrebbero anche mostrare due siti completamente diversi e distinti (cosa ovviamente sconsigliata).

Per fare in modo che chi tenta di visitare www.example.com venga rediretto a example.com, è necessario fare due cose:

  1. aggiungere www.example.com alla direttiva server_name, in modo che il virtual host gestisca entrambi i nomi;
  2. sfruttando le variabili, le istruzioni condizionali e la direttiva return, rimandare i client che richiedono www.example.com al sito example.com

Tradotto in termini di configurazione, il nostro file diventa il seguente:

events {}

http {

  include mime.types;  # direttiva per includere il file mime.types

  # Primo virtual host (sito)
  server {
    listen 80; # porta su cui il server è in ascolto;
               # se omessa, si assume sempre 80
    server_name example.com www.example.com;

    root /srv/example.com/public_html; # directory nella
              # quale sono posti i file che costituiscono il sito web

    if ($host != "example.com") {
        return 307 http://example.com$uri;
    }

    
  } # fine primo virtual host
} # fine contesto http

L’istruzione condizionale confronta il nome host con il quale è stato richiamanto il sito (contenuto nella variabile $host), e se non è uguale a “example.com” procede a ritornare un codice 307 (Temporary redirect) con la url da contattare. La url in questione contiene, oltre al nome host corretto (example.com, senza il www) anche la variabile $uri, in modo tale che se un client richiede la pagina www.example.com/product/book.html, questo viene rediretto correttamente a example.com/product/book.html, conservando quindi anche il resto della url che segue il nome host.

Se il sito fosse pubblicato anche tramite http over ssl (https), allora il redirect potrebbe rimandare direttamente alla versione “sicura” del sito invece che alla versione in chiaro.

Conclusioni

Rispetto al più popolare e largamente usato Apache, NGINX mostra indubbiamente una maggiore semplicità di configurazione, almeno quando si tratta di mettere in piedi un sito web che non ha necessità di produrre contenuti dinamicamente tramite php. Inoltre risulta essere estremamente veloce e meno avido di risorse del suo rivale, pur non offrendo ancora la sterminata quantità di moduli disponibili per Apache. La recente acquisizione da parte di F5 apporterà sicuramente vantaggi in termini di feature e di supporto, sperando che non ne comporti anche una progrressiva chiusura del codice come è già avvenuto per altri notevoli progetti open-source acquisiti da grosse corporation e poi scomparsi dalla circolazione per essere rilasciati soltanto sotto forma di software proprietario.

Utilizzando il sito, accetti l'utilizzo dei cookie da parte nostra. maggiori informazioni

Questo sito utilizza i cookie per fornire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o cliccando su "Accetta" permetti il loro utilizzo.

Chiudi