NGINX vs. Apache: i migliori web server open source a confronto

La prima versione del server Apache HTTP è stata rilasciata nel 1995. Ancora oggi, più di 20 anni dopo, il software risulta il principale web server, anche se non manca la concorrenza. Soprattutto il web server russo NGINX (pronunciato come “Engine X“), a sua volta un progetto open source, conquista una buona fetta di mercato e a una velocità sempre più crescente. Particolarmente doloroso per la Apache Foundation: la maggior parte dei siti che se la cavano bene nell’Alexa ranking utilizzano NGINX, come mostra una statistica regolarmente aggiornata di W3Techs. Ma non sono solo i giganti russi di Internet, come i motori di ricerca Rambler e Yandex, il servizio e-mail Mail.RU o il social network VK, a basarsi su questo web server leggero; anche siti internazionali come Dropbox, Netflix, WordPress e FastMail.com utilizzano NGINX per migliorare la performance dei loro servizi. Il server Apache HTTP ha quindi fatto il suo tempo?

N.B.

Il server Apache HTTP è un progetto software che viene portato avanti dalla Apache Foundation. Il nome del web server viene solitamente abbreviato con “Apache” dalla comunità di Internet. Anche noi seguiamo questa convenzione: quindi quando qui parliamo di “Apache” è inteso sempre il software e non il produttore.

La sfida del web 2.0

Owen Garrett, Head of Products a Nginx, Inc. descrive il web server Apache in un articolo del blog del 9 ottobre 2015 come “backbone” (letteralmente “colonna vertebrale”) del web 1.0 e mette in evidenza la sua importanza per lo sviluppo di Internet alle porte degli anni 2000. Il grande successo del web server sarebbe riconducibile soprattutto all’architettura semplice del software, basata su delle scelte di design che si orientavano al World Wide Web dell’epoca, diverso dalla sua evoluzione odierna. Infatti 20 anni fa le pagine web erano sostanzialmente create in maniera più semplice, la banda larga era ridotta e costosa, mentre il tempo di calcolo della CPU era proporzionalmente conveniente.

Oggi abbiamo a che fare con un World Wide Web di seconda generazione e ciò mostra un lato completamente differente: il numero degli utenti si è moltiplicato, come anche il traffico web mondiale. Lo stesso vale per le dimensioni medie delle pagine web in rete e per il numero dei componenti che un browser deve richiedere e di cui deve fare il rendering per poterli visualizzare. Una parte sempre più ampia della community di Internet è cresciuta con le possibilità offerte dal Web 2.0, perciò non è abituata ad aspettare diversi secondi o addirittura minuti per il caricamento di una pagina.

Negli ultimi anni questi sviluppi hanno posto il server Apache HTTP sempre di fronte a delle sfide. Owen Garrett ritiene perciò responsabile l’architettura basata sul processo di Apache, che non può essere ben scalata per quanto riguarda il traffico crescente. Questa vulnerabilità è stata una delle principali motivazioni per lo sviluppo di NGINX nel 2002 che con un’architettura event-driven (letteralmente “guidata dagli eventi”) propone delle altre vie. NGINX è stato creato dallo sviluppatore russo di software Igor Sysoev che ha adattato il software su misura per rispondere alle esigenze del motore di ricerca russo Rambler, ma allo stesso tempo viene anche utilizzato come web server, come reverse proxy e come e-mail proxy.

Nei prossimi paragrafi mettiamo a confronto i due web server e approfondiamo le loro differenze architetturali, la loro configurazione, le possibilità di estensione, oltre che alla loro compatibilità, alla loro documentazione e al supporto messo a disposizione.

Consiglio

Negli articoli di base su Apache e NGINX trovate un’introduzione generale a entrambi i web server open source messi a confronto, oltre che una guida sulla loro installazione e configurazione.

Differenze architetturali

I web server Apache e NGINX si basano su architetture software completamente diverse. Alla base vi sono quindi diversi modelli di gestione della connessione, dell’interpretazione delle richieste client, dell’uso di contenuti web statici e dinamici, oltre che della configurazione.

Gestione della connessione

Essenzialmente i web server open source Apache e NGINX si differenziano dal modo in cui trattano le richieste in entrata dei client (requests). Mentre alla base di Apache si trova un’architettura basata sul processo (process-based), su NGINX la gestione delle connessioni si basa su un algoritmo di elaborazione event-driven che consente di concludere delle richieste risparmiando risorse, anche se provengono da più connessioni contemporaneamente. Ciò viene indicato come un grande vantaggio rispetto al server Apache HTTP, specialmente dagli sviluppatori NGINX. Ma anche il server Apache offre la possibilità di implementare gli eventi a partire dalla versione 2.4. Di seguito le differenze in dettaglio.

Il web server Apache segue un approccio in cui ogni richiesta client viene elaborata da un processo separato o thread. Nel caso di un unico thread (single threading), la modalità di funzionamento originaria del server Apache HTTP, sorgono prima o poi dei problemi di blocco delle periferiche I/O: i processi che richiedono operazioni di scrittura e di lettura vengono elaborati rigorosamente uno dietro l’altro. Una richiesta successiva rimane in coda di attesa fino a quando la precedente non ha ricevuto risposta. Si può aggirare tutto questo avviando contemporaneamente più processi di single threading, una strategia che genera però un uso elevato delle risorse.

In alternativa vengono utilizzati i meccanismi di multithreading. A differenza del single threading dove in ogni processo è a disposizione un unico thread per rispondere alle richieste del client, il multithreading dà la possibilità di attivare più thread nello stesso processo. Visto che i thread su Linux, essendo dei processi, hanno bisogno di meno risorse, il multithreading permette di compensare il grande fabbisogno di risorse dell’architettura basata sui processi del server Apache.  

I meccanismi per l’elaborazione parallela si possono integrare sulle richieste client su Apache tramite uno dei tre moduli multi-processing (MPM): mpm_prefork, mpm_worker, mpm_event.

  • mpm_prefork: il modulo Apache “Prefork“ offre una gestione del multiprocesso sulla base di un meccanismo di single threading. Il modulo genera un processo padre che mette a disposizione una scorta di processi figli. In ogni processo figlio è attivo un thread che consente di rispondere rispettivamente a una richiesta client. Fin quando sono presenti più processi single thread che arrivano come richieste client, le request vengono elaborate contemporaneamente. Il numero dei processi disponibili di single threading viene definito con l’aiuto delle opzioni di configurazione server “MinSpareServers“ e “MaxSpareServers“. Prefork presenta gli svantaggi di performance nominati sopra in merito al single threading. È però vantaggiosa l’indipendenza completa di processi separati: se una connessione viene persa per via di un processo errato, di solito non si riflette sulle connessioni che vengono elaborate in altri processi.
  • mpm_worker: con il modulo “Worker“, Apache mette a disposizione un meccanismo di multithreading per l’elaborazione parallela delle richieste client. Il numero dei thread che possono venire avviati per processo si può definire grazie all’opzione della configurazione del server “ThreadsPerChild”. Il modulo prevede un thread per ogni connessione TCP. Fin quando sono a disposizione più thread che operano come richieste client, le request vengono portate a termine parallelamente. Il processo padre sorveglia sui thread non attivi (httpd).
    Gli utenti hanno a disposizione i comandi “MinSpareThreads“ e “MaxSpareThreads“ per definire da quale numero di thread non attivi vadano generati dei nuovi thread o debbano essere eliminati dalla memoria dei thread in funzione. Il modulo worker ha un fabbisogno di risorse essenzialmente inferiore rispetto al modulo prefork. Visto che le connessioni non vengono elaborate in processi separati, un thread errato può avere ripercussioni su tutto il processo di multithreading e quindi su tutte le connessioni che vengono elaborate all’interno dello stesso. Inoltre worker, come prefork, è anche soggetto a sovraccarichi tramite le cosiddette connessioni keep alive (v. sotto).  
  • mpm_event: a partire dalla versione 2.4, il server HTTP Apache ha a disposizione “Event”, un terzo modulo di multiprocessing per la produttività. Questo presenta una variante del modulo worker che si occupa di una distribuzione del carico tra i thread avviati. In aggiunta per ogni processo di multithreading viene usato un cosiddetto listener thread che accoglie le richieste client in entrata e distribuisce così i compiti correlati sui worker threads.
    Il modulo “Event” è stato sviluppato per ottimizzare le connessioni keep alive, quindi le connessioni TCP, che vengono mantenute per consentire la trasmissione di altre richieste client o risposte server (responses). Se viene utilizzato il classico modulo worker, i worker threads mantengono solitamente le connessioni instaurate e vengono così bloccate, anche quando non arrivano altre richieste. In caso di un numero elevato di connessioni keep alive è possibile che si verifichi un sovraccarico del server. Invece il modulo event memorizza le connessioni keep alive nei listener thread indipendenti. I worker thread non vengono così bloccati e sono a disposizione per l’elaborazione di altre richieste.

La seguente grafica indica una rappresentazione schematica dell’architettura basata su processi del web server Apache con utilizzo del modulo worker:

A seconda di quale modulo venga utilizzato, Apache risolve un problema di concorrenza, cioè la risposta contemporanea di più richieste client, tramite processi aggiuntivi o thread. Entrambe le strategie di risoluzione vanno di pari passo con una maggiore domanda di risorse aggiuntive, un fattore che diventa limitante nell’ambito del ridimensionamento del server Apache.

L’enorme richiesta di risorse dell’approccio di un processo per connessione deriva dal fatto che per ogni processo aggiuntivo deve essere messo a disposizione un proprio ambiente di runtime. Ciò richiede l’assegnazione del tempo di CPU e della memoria separata. Inoltre ogni modulo Apache, che deve essere messo a disposizione in un processo worker, viene caricato separatamente per ogni processo. Invece i thread si spartiscono un ambiente di esecuzione (il programma) e lo spazio di indirizzamento nella memoria. L’overhead di thread aggiuntivi è così notevolmente inferiore rispetto a quello dei processi. Ma anche il multithreading richiede un’elevata capacità di calcolo, quando si tratta di commutazione di contesto (context switch).

Come context switch si indica il processo in cui un sistema commuta da un processo o thread a un altro. Così il contesto del processo o thread concluso deve essere assicurato e quello del nuovo deve essere generato o ripristinato. Un processo amministrativo impegnativo in cui devono essere caricati e salvati il registro CPU, le diverse tabelle e liste.

Il modulo mpm_event mette a disposizione un meccanismo event per il server Apache che salva l’elaborazione delle connessioni in entrata in un listener thread. Questo consente di terminare le connessioni non più necessarie (anche connessioni keep alive) e di ridurre così l’uso delle risorse. Non viene però risolto il problema del context switch che richiede molte risorse e che si verifica quando le richieste del listener thread vengono trasmesse alle connessioni mantenute da questo ai worker thread separati. 

Invece l’architettura basata su eventi di NGINX realizza la concorrenza senza che sia necessario un processo o un thread aggiuntivo per ogni nuova connessione. Un singolo processo NGINX può elaborare migliaia di connessioni HTTP contemporaneamente. Questo viene realizzato tramite un meccanismo di cicli, il cosiddetto event loop. Ciò consente di portare a termine le richieste client asincronicamente all’interno di un thread.

Consiglio

Teoricamente NGINX se la cava nell’elaborazione delle connessioni solo con un processo di single threading. Per sfruttare in modo ottimale l’hardware, il web server viene di solito però avviato con un processo worker per ogni core del processore (CPU) della macchina alla base.

A differenza del web server Apache in cui si può delimitare il numero dei processi attivi e dei thread solo tramite valori minimi e massimi, NGINX offre un modello di processo prevedibile che viene regolato esattamente sull’hardware a disposizione. Questo comprende un processo master, i processi di aiuto cache loader e cache manager, oltre che un numero di processi worker adattati al numero dei core del processore e stabiliti con precisione attraverso la configurazione.

  • Processo master: con il processo master si indica un processo sovraordinato che esegue tutte le operazioni basilari. Tra queste rientrano ad esempio l’inserimento della configurazione server, il collegamento della porta, oltre che la creazione di tutti gli altri tipi di processo seguenti.
  • Processi di aiuto: NGINX utilizza due processi di aiuto per la gestione della cache, cioè il cache loader e il cache manager.
    • Cache loader: il cache loader è responsabile del fatto che la cache basata sul disco fisso venga caricata nella RAM.
    • Cache manager: il compito del cache manager è di prestare attenzione al fatto che le voci della cache del disco fisso presentino le dimensioni configurate prima e le riducano quando necessario. Questo processo avviene periodicamente.
  • Processo worker: i processi worker sono responsabili dell’elaborazione delle connessioni, dei permessi di scrittura e di lettura sul disco fisso, oltre che della comunicazione con i server upstream (server che mettono a disposizione servizi per gli altri server). Si tratta così degli unici processi del modello del processo NGINX che sono di soliti attivi.

La seguente grafica indica una rappresentazione schematica del modello del processo NGINX:

Tutti i processi worker, avviati dal processo master NGINX durante la configurazione, si spartiscono un set di listener socket (endpoint di comunicazione). Al posto di avviare per ogni connessione in entrata un proprio processo o thread, in ogni processo worker viene eseguito un event loop che consente l’elaborazione asincrona di diverse migliaia di connessioni all’interno di un thread senza bloccare il processo. In più i processi worker ascoltano i listener socket continuamente per rilevare gli eventi provocati dalle connessioni in entrata, le accolgono e durante la modifica delle richieste HTTP eseguono processi di scrittura e lettura nel socket.

Così NGINX non mette a disposizione nessun proprio meccanismo per la distribuzione delle connessioni sui processi worker. Al suo posto vengono utilizzate le funzioni kernel del sistema operativo. Gli schemi, che indicano come vanno elaborate le richieste in entrata, vengono messi a disposizione tramite macchine di stato separate (state machine) per HTTP, raw TCP, SMTP, IMAP e POP3.

In generale NGINX può essere indicato come un event handler che riceve informazioni sugli eventi dal kernel e dice al sistema operativo come si devono elaborare i compiti correlati. L’elaborazione asincrona dei compiti all’interno degli event loop si basa su notifiche di eventi, funzioni di chiamata (callback) e timer. Questi meccanismi permettono a un processo worker di delegare un’operazione dopo l’altra al sistema operativo senza dover attendere passivamente il risultato di un’operazione o la risposta dei client. NGINX funge così da orchestratore per il sistema operativo, che si fa carico della lettura e della scrittura di byte.

Questo tipo di gestione delle connessioni genera solo un overhead minimale per connessioni aggiuntive. Tutto ciò che serve è un ulteriore File Descriptor (FD) e un minimo di RAM aggiuntiva nel processo worker. I context switch che richiedono molte risorse, invece, sorgono solo quando non si verificano altri eventi all’interno di un event loop. Questa efficacia nell’elaborazione delle richieste su innumerevoli connessioni predestina NGINX a svolgere la funzione di load balancer per i siti molto frequentati, come WordPress.com.

In sintesi

Con l’architettura basata su eventi NGINX offre un’alternativa alla gestione delle connessioni basata su processi del server Apache. Ma questa caratteristica da sola non basta a dare una spiegazione del motivo per cui NGINX se la cavi così bene nei test di benchmark. Infatti anche Apache supporta un meccanismo di elaborazione basato su eventi per le richieste client a partire dalla versione 2.4. Nel caso dei confronti di web server, come Apache vs. NGINX, prestate sempre attenzione con quali moduli il server web viene utilizzato nel test, come viene configurato il web server e quali compiti deve compiere.

Come comportarsi con i contenuti web statici e dinamici

Anche quando ha a che fare con contenuti web dinamici, NGINX segue un’altra strategia rispetto al server Apache.

Essenzialmente vale il fatto che per poter consegnare i contenuti web dinamici, un web server deve ricorrere a un interprete che sia in grado di elaborare un linguaggio di programmazione necessario come PHP, Perl, Python o Ruby. Apache mette perciò diversi moduli a disposizione, come mod_php, mod_perl, mod_python o mod_ruby, che consentono di caricare il rispettivo interprete direttamente nel web server. Così Apache stesso possiede la facoltà di elaborare i contenuti web dinamici che sono stati creati con il rispettivo linguaggio di programmazione. Funzioni come la disponibilità di contenuti statici vengono già implementate con i moduli MPM nominati sopra.

Invece NGINX offre solo meccanismi per consegnare contenuti web statici. La messa a disposizione dei contenuti dinamici viene perciò immagazzinata sugli application server specializzati al riguardo. NGINX funge in questo caso solo da proxy tra il client e l’upstream server. La comunicazione avviene tramite protocolli come HTTP, FastCGI, SCGI, uWSGI e Memcached. Come possibili application server per la consegna di contenuti dinamici si prestano WebSphere, JBoss o Tomcat, ma anche il server Apache può essere utilizzato per questo scopo.

Entrambe le strategie nel trattare i contenuti dinamici e statici sono legati a vantaggi e svantaggi. Un modulo come mod_php consente al web server di eseguire da solo il codice PHP; non è necessario un application server separato. Ciò rende molto pratica l’amministrazione delle pagine dinamiche. I moduli di interprete per i linguaggi di programmazione dinamici devono però essere caricati separatamente in ogni processo worker che concorre alla visualizzazione del contenuto. Nel caso di una grande quantità di processi worker ciò va di pari passo con un chiaro overhead che riduce NGINX, visto che l’interprete delocalizzato viene interpellato solo se necessario.

Mentre NGINX dipende dall’interazione con un interprete esterno, agli utenti Apache rimangono a disposizione entrambe le strategie. Anche Apache può essere applicato a un application server che si fa carico dell’interpretazione di contenuti web dinamici. Solitamente per questo viene utilizzato il protocollo FastCGI e con il modulo mod_proxy_fcgi si può caricare una relativa interfaccia.

In sintesi

Con entrambi i web server nel nostro confronto si possono visualizzare pagine web dinamiche. Ma mentre il programma del codice Apache viene interpretato ed eseguito autonomamente grazie a dei moduli, NGINX sposta questo passaggio su un application server esterno.

Interpretazione di richieste client

Per poter rispondere adeguatamente ai programmi client (ad esempio browser o programmi di posta elettronica), un server deve individuare, sulla base delle richieste, di quale risorsa necessita e dove si trova.

Il server Apache è stato concepito come web server, invece NGINX svolge sia funzioni da web server che da proxy server. Questa differenza nel focus si riflette anche nel modo in cui il software interpreta le richieste client e assegna le risorse sul server.

N.B.

Anche il server Apache si può utilizzare come proxy server grazie all’aiuto del modulo mod_proxy.

I server Apache e NGINX dispongono entrambi di meccanismi che consentono di interpretare le richieste in entrata sia come risorse fisiche nel file system sia come URI (Uniform Resource Identifier). Mentre Apache lavora di default basandosi sui file, su NGINX occupa una posizione di primo piano l’elaborazione delle richieste basata su URI. 

Se giunge una richiesta client al server Apache, questo parte regolarmente dal presupposto che una precisa risorsa deve essere richiamata dal file system del server. Visto che Apache offre la possibilità tramite VirtualHosts di mettere a disposizione diversi contenuti web sullo stesso server con nomi host, indirizzi IP e numeri di porta differenti, deve prima di tutto essere individuato a quale VirtualHost si riferisca la richiesta. Così il web server confronta il nome host, l’indirizzo IP e il numero di porta all’inizio della richiesta URI con i VirtualHosts definiti nel file principale di configurazione httpd.conf.

Il seguente codice di esempio indica una configurazione Apache in cui vengono gestiti entrambi i domini www.example.com e www.other-example.com con lo stesso indirizzo IP:

NameVirtualHost *:80
<VirtualHost *:80>
ServerName www.example.com
ServerAlias example.com *.example.com
DocumentRoot /data/www/example
</VirtualHost>
<VirtualHost *:80>
ServerName www.other-example.com
DocumentRoot /data/www/other-example
</VirtualHost>

L’asterisco (*) serve come segnaposto per un qualsiasi indirizzo IP. È Apache a decidere in quale DocumentRoot (la cartella di partenza di un progetto web) viene ricercata la risorsa richiesta, confrontando la direttiva ServerName nel nome host contenuto nella richiesta.

Se Apache ha trovato il server desiderato, il request URI verrà riprodotto di default sul file system del server (mapping). A questo scopo Apache utilizza il percorso contenuto nell’URI. In combinazione con la DocumentRoot si ottiene il percorso per giungere alla risorsa.

In una richiesta con un request URI del tipo "http://www.example.org:80/public_html/images/logo.gif" Apache verrebbe ricercato (partendo dall’esempio esposto sopra) sotto al percorso seguente secondo la risorsa desiderata:

/data/www/example/public_html/images/logo.gif
N.B.

Visto che 80 è la porta standard per il protocollo HTTP, solitamente viene omessa.

Inoltre Apache confronta il request URI con blocchi di file e directory opzionali nella configurazione. Questi consentono di definire delle direttive specifiche per le richieste che si riferiscono alle cartelle o ai file scelti (incluse le sottocartelle).

Nel seguente esempio vengono definite delle direttive specifiche per la cartella public_html/images e il file private.html:

<VirtualHost *:80>
    ServerName www.example.com
    ServerAlias example.com *.example.com
    DocumentRoot /data/www/example
      <Directory var/www/example.com/public_html/images>
          Order Allow,Deny
          Allow from all
     </Directory> 
      <Files public.html>
          Order Allow,Deny
          Deny from all
      </Files>
</VirtualHost>

Oltre a questo procedimento standard, Apache nell’interpretazione delle richieste client con la direttiva alias dà la possibilità di indicare una cartella alternativa in cui ricercare la risorsa richiesta al posto che nella DocumentRoot. Inoltre il server Apache mette a disposizione con mod_rewrite un modulo che consente agli utenti di riscrivere gli URL o di inoltrarli.

Consiglio

Trovate maggiori informazioni al riguardo nel nostro articolo sulle basi del Rewrite Engine.

Se Apache deve richiamare delle risorse che sono state create al di fuori del file system, viene usata la direttiva location che consente di definire le direttive per URI precisi.

Ma quello che è un’eccezione su Apache, su NGINX è un caso da manuale. NGINX analizza prima di tutto il request URI e lo confronta con i blocchi server e location nella configurazione del web server. Solo dopo si trova (se necessario) un mapping sul file system e la combinazione con la root (che corrisponde alla DocumentRoot del server Apache).

Grazie alla direttiva server NGINX individua quale host è responsabile per la risposta della richiesta client. Il blocco del server corrisponde così a un VirtualHost nella configurazione Apache. In aggiunta il nome host, l’indirizzo IP e il numero di porta del Request URI vengono confrontati con tutti i blocchi server definiti nella configurazione del web server. Il seguente codice di esempio mostra tre blocchi server all’interno del file di configurazione NGINX nginx.conf:

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;
    ...
}
N.B.

Di solito ogni blocco server comprende una serie di blocchi location. Nell’esempio attuale sono stati sostituiti da un segnaposto (…).

Un confronto del request URI con i blocchi location all’interno di un blocco server avviene solo quando viene trovato il server richiesto. Così NGINX inserisce i blocchi location presenti e cerca la location che corrisponde di più con quella del request URI. Ogni blocco location è costituito da direttive specifiche che indicano a NGINX come deve essere elaborata la richiesta corrispondente.

Così le location possono essere definite di modo che vengano interpretate come prefisso per un percorso, come coincidenza esatta o espressione regolare (Regular Expression, RegEx). Inoltre nella sintassi della configurazione server vengono utilizzati i seguenti modificatori:

Nessun modificatore La location viene interpretata come prefisso. Tutte le richieste il cui URI presenta il prefisso definito nella direttiva location valgono come coincidenti con la location. Se non si trova nessuna location più specifica, la richiesta verrà elaborata a seconda delle indicazioni in questo blocco location.
= La location viene interpretata come coincidenza esatta. Tutte le richieste il cui URI coincide esattamente con la sequenza di caratteri presentata nella direttiva location, vengono elaborate secondo le indicazioni contenute in questo blocco location.
 ~ La location viene interpretata come espressione regolare. Tutte le richieste il cui URI coincide con l’espressione regolare vengono elaborate a seconda delle indicazioni contenute in questo blocco location. Durante il confronto vengono prese in considerazione le scritte in maiuscolo e minuscolo (case-sensitive).
~* La location viene interpretata come espressione regolare. Tutte le richieste il cui URI coincide con l’espressione regolare vengono elaborate a seconda delle indicazioni contenute in questo blocco location. Durante il confronto non vengono prese in considerazione le scritte in maiuscolo e minuscolo (case-insensitive).

Nell’esempio seguente i tre blocchi location indicano il modo di elaborazione delle richieste in entrata per i domini example.org e www.example.org:

server {
    listen 80;
    server_name example.org www.example.org;
    root /data/www;
    location / {
        index index.html index.php;
    }
    location ~* \.(gif|jpg|png)$ {
        expires 30d;
    }
location ~ \.php$ {
        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

Partendo da una richiesta client con il request URI "http://www.example.org:80/logo.gif",NGINX procederebbe nel modo seguente per interpretare le richieste seguenti e far visualizzare la risorsa desiderata:

http://www.example.org:80/logo.gif
http://www.example.org:80/index.php

NGINX individua prima di tutto la location prefisso più specifica. Inoltre il web server inserisce tutte le location senza modificatore della serie e si ferma quando trova la prima location che coincide con la richiesta. Infine vengono inserite tutte le location che sono contrassegnate con il modificatore RegEx (~­). Anche qui viene usato il primo match. Se non si trova nessuna location RegEx, il web server ricorre alla location prefisso individuata precedentemente come fallback.

Il request URI www.example.org:80/logo.gif coincide ad esempio sia con la location prefisso / che con l’espressione regolare \.(gif|jpg|png)$. NGINX riprodurrebbe perciò la richiesta in combinazione con la root sul percorso di file /data/www/logo.gif e consegnerebbe la risorsa corrispondente al client. L’head “expires” indica quando una risposta non è più da considerare valida, ovvero nell’esempio attuale dopo 30 giorni: expires 30d.

Anche la richiesta della pagina PHP con l’URI www.example.org:80/index.php coincide con la location prefisso / oltre che con la location RegEx ~ \.php$, che viene trattata per prima. NGINX trasmette perciò la richiesta a un server FastCGI che ascolta su localhost:9000 ed è responsabile dell’elaborazione dei contenuti dinamici. Così la direttiva fastcgi_param imposta il parametro FastCGI SCRIPT_FILENAME su /data/www/index.php. Infine il file viene eseguito sul server upstream. In questo modo la variabile $document_root corrisponde alla direttiva root, la variabile $fastcgi_script_name a una parte dell’URI che segue sul nome host e il numero della porta: /index.php.

Questo procedimento, utilizzato durante l’interpretazione delle richieste client, risulta un po‘ complicato al primo sguardo e ciò è imputabile ai diversi campi di applicazione nei quali viene usato NGINX. Paragonato al procedimento primario basato sui file del server Apache HTTP, l’interpretazione delle richieste basata su URI consente una maggiore flessibilità nell’elaborazione dei diversi schemi di richiesta. Ciò viene ad esempio richiesto quando NGINX non funge da web server, bensì da proxy server o da mail proxy server.

In sintesi

Apache viene utilizzato prima di tutto come web server e interpreta primariamente le richieste client basate sui file. Invece NGINX di solito lavora con URI e sottostà così anche ad altri schemi di richiesta.

Configurazione

Al contrario del server Apache, a NGINX si attribuisce una maggiore velocità nella visualizzazione dei contenuti web statici, riconducibile a differenze nella configurazione. Il web server Apache offre agli amministratori la possibilità di una gestione sul livello delle cartelle, oltre che il file principale di configurazione httpd.conf. In aggiunta vengono utilizzati i cosiddetti file .htaccess. Questi file di configurazione decentralizzati si possono principalmente implementare in qualsiasi cartella server. Le direttive che vengono definite in un .htaccess si riferiscono sia alla cartella che comprende il file di configurazione che alle relative sottocartelle. In pratica vengono utilizzati i file .htaccess per limitare l’accesso alla cartella a determinate cerchie di utenti, per proteggerla con una password, oltre che per definire regole per la ricerca nelle cartelle, per mostrare gli avvisi di errori e impostare gli inoltri o la visualizzazione di contenuti alternativi. È necessario prestare attenzione al fatto che tutto questo si possa configurare centralmente nel file httpd.conf. Il file .htaccess diventa però rilevante nei modelli hosting come quelli di hosting condiviso dove l’accesso al file principale di configurazione è riservato al provider. La configurazione decentralizzata tramite .htaccess consente agli utenti l’amministrazione per i diversi ambiti del file system del server, ad esempio per le cartelle di progetto scelte, senza dargli accesso alla configurazione principale. Inoltre le modifiche vengono applicate sin da subito e senza dover riavviare il server. Invece NGINX offre solo la possibilità di configurazioni centrali. Tutte le direttive vengono definite nel file nginx.conf. Con l’accesso a questo file un utente riceve il controllo sull’intero server. A differenza di Apache, l’accesso amministrativo non si può limitare alle cartelle selezionate. Da ciò ne derivano vantaggi e svantaggi. Infatti la configurazione centrale di NGINX è meno flessibile rispetto a quella del server Apache, ma offre un sostanziale beneficio sul fronte della sicurezza: le modifiche alla configurazione del web server possono essere apportate solo dagli utenti che dispongono dei permessi di root. Ma più importante rispetto all’argomento sulla sicurezza è lo svantaggio di performance di una configurazione decentrata tramite .htaccess. Già nella documentazione del server Apache gli sviluppatori consigliano di rinunciare all’uso di .htaccess non appena è possibile l’accesso al file httpd.conf. Il motivo di ciò è la procedura che inserisce e interpreta i file di configurazione Apache. Come già scritto, Apache segue di default uno schema basato sui file per rispondere alle richieste dei client. Visto che l’architettura Apache consente una configurazione decentrata, il web server analizza il file .htaccess ricercando la risorsa richiesta in ogni cartella lungo il percorso del file. Tutti i file di configurazione che attraversa vengono inseriti e interpretati; questo schema rallenta notevolmente il web server.

N.B.

In linea di principio gli amministratori Apache sono lasciati liberi di scegliere se ricorrere a opzioni di configurazione decentralizzate del web server e se vogliono prendere in considerazione i relativi vantaggi e svantaggi. Nella documentazione gli sviluppatori mettono in risalto il fatto che tutte le configurazioni .htaccess possono essere apportate per mezzo dei blocchi di directory anche nella configurazione principale httpd.conf.

Per questo gli utenti che vorrebbero disattivare o limitare la configurazione decentralizzata su Apache utilizzano la direttiva AllowOverride nei blocchi delle directory del file principale di configurazione httpd.conf e lo impostano su None. Ciò segnala al web server di ignorare tutti i file .htaccess nelle cartelle configurate corrispondentemente.

<VirtualHost *:80>
    ServerName example.com;
    ...
    DocumentRoot /data/www/example
      <Directory /data/www/example>
        AllowOverride None
        ...
      </Directory>
    ...
</VirtualHost>

Nello specifico la configurazione di esempio impone al web server di ignorare tutti i file .htaccess per l’host example.com.

In sintesi

A differenza di NGINX che viene configurato solo centralmente, Apache offre con il file .htaccess la possibilità di impostare una configurazione decentrata e basata sulle cartelle. Se vengono utilizzati i file .htaccess, il web server perde però velocità.

Possibilità di estensioni

Entrambi i web server del nostro confronto si basano su un sistema modulare grazie al quale il nucleo del software può essere ampliato con altri componenti aggiuntivi, se necessario. Ma fino alla versione 1.9.10 NGINX seguiva per la gestione dei moduli un’altra strategia essenziale rispetto al prodotto concorrente.

Il server Apache HTTP mette a disposizione due possibilità di ampliare il nucleo del software. I moduli si possono compilare durante lo sviluppo nei file binari di Apache o si possono caricare dinamicamente e quindi durante il tempo di esecuzione.

Si possono distinguere tre categorie di moduli Apache:

  • Moduli di base: i moduli di base di Apache comprendono tutti i componenti che mettono a disposizione le funzionalità principali del web server.
  • Moduli di estensione: queste estensioni sono dei moduli di Apache Foundation che vengono installati come parte di Apache Distribution. Nella documentazione Apache in inglese si trova un riepilogo generale su tutti i moduli contenuti nell’installazione standard di Apache 2.4.
  • Moduli di terze parti: questi moduli non vengono messi a disposizione dalla Apache Foundation, ma da altri fornitori esterni o da programmatori autonomi.

Invece su NGINX la modularità è da tempo limitata alle estensioni statiche che dovevano essere compilate nel file binario del nucleo del software. Proprio per gli utenti che non erano abituati a gestire alcuni componenti software senza il gestore di pacchetti della rispettiva distribuzione, questo tipo di estensione limitava notevolmente la flessibilità del web server. Il team di sviluppatori ha migliorato questo punto: a partire dalla versione 1.9.11 (release del 09.02.2016), NGINX supporta meccanismi che consentono di convertire moduli statici in dinamici, così che questi possano essere caricati durante il tempo di esecuzione tramite i file di configurazione. In entrambi i casi viene utilizzato il modulo API del server.

Tuttavia è necessario prestare attenzione al fatto che non tutti i moduli NGINX possono essere convertiti in moduli dinamici. Infatti i moduli che servono a mettere una patch al codice sorgente del server non dovrebbero essere caricati dinamicamente. Inoltre NGINX limita nell’impostazione standard il numero di moduli dinamici che possono essere caricati contemporaneamente a 128. Per aumentare questo valore limite, impostate la costante di NGX_MAX_DYNAMIC_MODULES nel codice sorgente di NGINX sul valore desiderato.

In aggiunta ai moduli ufficiali della documentazione NGINX sono a disposizione degli utenti diversi moduli di terze parti.

Summary

Entrambi i web server si possono ampliare con moduli. Oltre ai moduli statici sono messi a disposizione quelli dinamici, che possono venire caricati nel programma in funzione, se necessario.

Documentazione e supporto

Entrambi i progetti software sono ben documentati e forniscono informazioni di prima mano agli utenti grazie a wiki e blog.

Mentre la documentazione di NGINX è disponibile solo in inglese e russo, il progetto Apache si contraddistingue per i suoi materiali informativi in numerose lingue, anche se manca l’italiano. Inoltre è da notare che le guide non sono sempre aggiornate ed è quindi imprescindibile dare un’occhiata alla documentazione in inglese anche in questo caso. Gli utenti ricevono aiuto in caso di problemi su entrambi i progetti open source dalla community. Così le mailing list fungono da forum di discussione.

I piani di release trasparenti e le roadmap danno agli utenti la possibilità di installare gli sviluppi futuri. Gli errori software e le vulnerabilità vengono registrate su entrambi i progetti in un report pubblico di bug e quindi elaborati.

In aggiunta al progetto open source NGINX Nginx, Inc. offre il prodotto commerciale NGINX Plus. Pagando una quota d’uso annuale gli utenti si assicurano delle funzioni aggiuntive così come un supporto professionale a carico del produttore. Sul sito di NGINX trovate una tabella di confronto di entrambi i prodotti. Non esiste un’edizione commerciale del server Apache. I servizi di supporto a pagamento vengono offerti, invece, da diversi fornitori esterni.

In sintesi

Sia per il server Apache che per quello NGINX si trova una ricca documentazione per un uso professionale sui sistemi produttivi.

Compatibilità ed ecosistema

Il server Apache caratterizza il World Wide Web da più di due decadi e per via delle sua quota di mercato rimane ancora lo standard per eccellenza per mettere a disposizione i contenuti web. Anche NGINX può guardare indietro a una storia di successi lunga 15 anni. Entrambi i web server si contraddistinguono per un vasto supporto di diverse piattaforme. Mentre Apache è consigliato per tutti i sistemi operativi compatibili con Unix e Windows, la documentazione NGINX indica che il web server è stato testato sui seguenti sistemi operativi: FreeBSD, Linux, Solaris, IBM AIX, HP-UX, macOS e Windows. Come server standard Apache emerge per un’estensiva compatibilità con i progetti di terze parti. Tutti gli standard web rilevanti si possono integrare tramite moduli. In più è da considerare che la maggior parte delle persone in rete hanno dimestichezza con Apache. Di solito gli amministratori e gli sviluppatori web impostano i loro primi progetti su piattaforme convenienti di hosting condiviso, che si basano a loro volta per la maggior parte su Apache e danno l’opportunità di una configurazione decentralizzata tramite .htaccess. Inoltre il server Apache fa parte di diversi pacchetti di programmi open source per lo sviluppo ed è presente nei test software come XAMPP o AMPPS. Anche NGINX mette a disposizione degli utenti un grande ecosistema di moduli. Inoltre il team degli sviluppatori si occupa di intrattenere collaborazioni con i diversi progetti software open source e proprietari, oltre che con servizi di infrastruttura come Amazon Web Services, Windows Azure e HP.

In sintesi

Entrambi i web server sono affermati. Gli utenti possono ricorrere a un grande ecosistema. Apache ha il vantaggio, rispetto a NGINX, di aver incluso nel corso degli anni una grande community di utenti che si attiva per garantire le basi del web server. Tuttavia il fatto che migliaia di amministratori abbiano controllato e migliorato il codice sorgente del software non garantisce la sicurezza del web server. Anche i nuovi utenti beneficiano dell’enorme numero di amministratori Apache esperti che aiutano la community in caso di problemi sul forum o tramite mailing list.

NGINX vs. Apache: differenze a confronto

Malgrado le grandi differenze nell’architettura software, entrambi i web server offrono simili funzioni. Apache e NGINX vengono utilizzati in scenari simili, ricorrono però a proprie strategie e modelli per rispondere ai requisiti richiesti. La seguente tabella mette a confronto entrambi i progetti software considerandone le caratteristiche principali e indicando similitudini e divergenze.

Caratteristica Apache NGINX
Funzione Web server Proxy server Web server Proxy server E-Mail proxy Load balancer
Linguaggio di programmazione C C
Sistema operativo Tutte le piattaforme compatibili con Unix Windows FreeBSD Linux Solaris IBM AIX HP-UX macOS Windows
Data di rilascio 1995 2002
Licenza Apache License v2.0 Licenza BSD (Berkeley Software Distribution)
Sviluppatore Apache Software Foundation Nginx, Inc.
Architettura software Basato su processi/thread Basato su eventi/event-driven
Concorrenza Multiprocessing Multithreading Event loop
Contenuti web statici
Contenuti web dinamici No
Interpretazione delle richieste client Principalmente basata su file Basata su URI
Configurazione Configurazione centralizzata tramite httpd.conf Configurazione decentralizzata tramite.htaccess   Configurazione centralizzata tramite nginx.conf
Estensioni Moduli statici Moduli dinamici Moduli statici Moduli dinamici
Documentazione Tedesco Inglese Danese Spagnolo Francese Giapponese Coreano Portoghese Turco Cinese Tedesco Inglese  
Supporto da parte dello sviluppatore No Sì (a pagamento su Nginx, Inc.)
Supporto della community Mailing list Wiki Mailing list Wiki

Conclusione

Con Apache e NGINX gli utenti hanno a disposizione due progetti open source stabili e sicuri. Nessuno dei due web server ne emerge però vincitore assoluto. Alla base di entrambi i progetti si trovano delle scelte di design essenzialmente diverse che, a seconda di come viene utilizzato il software, implicano vantaggi e svantaggi.

Il server Apache HTTP offre un immenso repertorio di moduli che apre al software grazie a opzioni di configurazione flessibili innumerevoli campi di applicazione. Il web server rappresenta il software standard per scenari di hosting condiviso e in questo ambito si affermerà anche sui web server leggeri come NGINX. La possibilità di integrare degli interpreti direttamente nel web server, tramite moduli per i linguaggi di programmazione come PHP, Perl, Python o Ruby, permette la consegna dei contenuti web dinamici senza dover ricorrere a un application server separato. Ciò rende il server Apache una soluzione pratica per siti piccoli e medio-grandi in cui i contenuti vengono generati dinamicamente durante il suo utilizzo.

Al contrario NGINX non offre nessuna possibilità di elaborare i contenuti dinamici di per sé o di integrare degli interpreti corrispondenti tramite moduli. Così è sempre necessario un application server separato, cosa che potrà sembrare uno sforzo inutile nel caso di siti piccoli e medio-grandi. Una struttura simile mostra però i suoi punti di forza in presenza di grandi progetti web e di un aumento del traffico.

Solitamente NGINX viene utilizzato come load balancer per un gruppo di application server. Così il load balancer accoglie le richieste e decide a seconda del loro tipo se queste devono essere inoltrate a un server specializzato che opera in background. I contenuti web statici vengono consegnati direttamente da NGINX. Invece, se un client richiede dei contenuti dinamici, il load balancer inoltra la richiesta a uno degli application server previsti per questo scopo. Questo server interpreta il linguaggio di programmazione, riunisce i contenuti richiesti in una pagina web e li restituisce a un load balancer che si fa carico di nuovo della consegna al client. In questo modo si possono gestire efficacemente flussi di traffico elevati.

In più NGINX memorizza già i contenuti scaricati nella cache per un determinato periodo, di modo che i contenuti dinamici richiesti possano venire consegnati di nuovo direttamente dal load balancer senza che NGINX debba ricorrere nuovamente all’application server.

La delocalizzazione dell’interprete su uno o più server back end separati ha il vantaggio che si riesce a scalare comodamente la serie di server, utilizzando server back end aggiuntivi, se necessario, o potendo disattivare i sistemi che non servono. In pratica molti utenti utilizzano una struttura di questo tipo, combinando NGINX e Apache e approfittando così dei punti di forza di entrambi i web server.

Hai trovato questo articolo utile?
Per offrirti una migliore esperienza di navigazione online questo sito web usa dei cookie, propri e di terze parti. Continuando a navigare sul sito acconsenti all’utilizzo dei cookie. Scopri di più sull’uso dei cookie e sulla possibilità di modificarne le impostazioni o negare il consenso.
Page top