Come configurare HTTP/2 e redirect HTTPS su Nginx
Nonostante la bontà di Nginx, la configurazione originale lascia spazio a numerosi miglioramenti.
Cos'è Nginx?
Nginx è un server web ad alte prestazioni utilizzato molto spesso in coppia con Apache. Quando utilizzato da solo, soprattutto per servire siti web statici Nginx dà il meglio.
HTTP/2 e redirect HTTPS su Nginx: il problema
Una tipica configurazione di default (senza modifiche particolari) per un virtual host Nginx è di solito la seguente:
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89;
root /home/nome-dominio/public_html;
index index.html index.htm index.php;
access_log /var/log/virtualmin/nome-dominio.it_access_log;
error_log /var/log/virtualmin/nome-dominio.it_error_log;
listen 192.168.80.89:443 ssl;
ssl_certificate /home/nome-dominio/ssl.combined;
ssl_certificate_key /home/nome-dominio/ssl.key;
}
Possiamo distinguere tra queste righe i due host e l'ip su cui la configurazione risponde:
server {
# omesso
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89;
# omesso
}
La home directory per i file del sito ed i log di accesso/errore:
server {
# omesso
root /home/nome-dominio/public_html;
index index.html index.htm index.php;
access_log /var/log/virtualmin/nome-dominio.it_access_log;
error_log /var/log/virtualmin/nome-dominio.it_error_log;
}
ed infine la configurazione SSL:
server {
# omesso
listen 192.168.80.89:443 ssl;
ssl_certificate /home/nome-dominio/ssl.combined;
ssl_certificate_key /home/nome-dominio/ssl.key;
}
Una configurazione come questa non offre:
- HTTP/2
- redirect da http ad https (superfluo spendere parole sull'importanza del certificato SSL)
- eventuale redirect da non www a www.
Vediamo come porre rimedio.
NOTA: le ultime versioni di Nginx su alcuni sistemi operativi includono già HTTP/2 di default.
Step 1: redirect HTTPS su Nginx
Requisito essenziale per questo passaggio è la presenza di un certificato SSL, che ormai è possibile ottenere facilmente con Let's Encrypt.
Per impostare un redirect da http ad https su Nginx dobbiamo prima di tutto organizzare la configurazione in due blocchi server
.
Il primo blocco è in ascolto per servire le richieste non crittografate che devono immediatamente essere redirette verso la versione https:
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89;
return 301 https://$host$request_uri;
}
Già in questa fase è possibile verificare la configurazione riavviando Nginx:
systemctl restart nginx
E poi usando curl per contattare il web server:
curl -I -v nome-dominio.it
In questo primo check dovresti vedere qualcosa di simile, segno che il redirect sta funzionando:
HTTP/1.1 301 Moved Permanently
Server: nginx/1.14.0 (Ubuntu)
Date: Thu, 02 Apr 2020 12:59:48 GMT
Content-Type: text/html
Content-Length: 194
Connection: keep-alive
Location: https://nome-dominio.it/
Ora, nel secondo blocco server
configuriamo la parte HTTPS vera e propria:
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89:443 ssl;
ssl_certificate /home/nome-dominio/ssl.combined;
ssl_certificate_key /home/nome-dominio/ssl.key;
root /home/nome-dominio/public_html;
index index.html index.htm index.php;
access_log /var/log/virtualmin/nome-dominio.it_access_log;
error_log /var/log/virtualmin/nome-dominio.it_error_log;
}
È qui che configuriamo le direttive ssl_certificate
, ssl_certificate_key
e listen
con ssl.
Ecco la configurazione completa:
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89;
return 301 https://$host$request_uri;
}
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89:443 ssl;
ssl_certificate /home/nome-dominio/ssl.combined;
ssl_certificate_key /home/nome-dominio/ssl.key;
root /home/nome-dominio/public_html;
index index.html index.htm index.php;
access_log /var/log/virtualmin/nome-dominio.it_access_log;
error_log /var/log/virtualmin/nome-dominio.it_error_log;
}
Come suggerimento generale, dopo ogni modifica è opportuno verificare la configurazione di Nginx con:
nginx -t
Questo consente di evitare situazioni scottanti ...
Solo se la configurazione non ritorna errori è possibile riavviare Nginx con:
systemctl restart nginx
Step 2: redirect da non www a www
Questione di preferenza personale, requisiti specifici, o questione di SEO, il redirect di un sito da non www a www è spesso gradito.
Per questo step non sono necessarie grandi modifiche, se non intervenire nel blocco server
che controlla l'ssl:
#omesso
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89:443 ssl http2;
if ($host = 'nome-dominio.it') {
return 301 https://www.$host$request_uri;
}
ssl_certificate /home/nome-dominio/ssl.combined;
ssl_certificate_key /home/nome-dominio/ssl.key;
root /home/nome-dominio/public_html;
index index.html index.htm index.php;
access_log /var/log/virtualmin/nome-dominio.it_access_log;
error_log /var/log/virtualmin/nome-dominio.it_error_log;
}
Ci sono sicuramente soluzioni più eleganti di questa proposta:
if ($host = 'nome-dominio.it') {
return 301 https://www.$host$request_uri;
}
In alternativa è possibile configurare un terzo blocco server
che gestisce la versione non www del sito, ritornando un 301 verso la version www servita da un blocco server
a parte.
Ma se non altro svolge bene il suo lavoro.
Dopo il completamento di questo passaggio è possibile verificare la configurazione di Nginx prima di applicare le modifiche:
nginx -t
per poi riavviare Nginx con:
systemctl restart nginx
A questo punto con curl, possiamo usare il flag -L
per seguire i redirect della nuova configurazione:
curl -I -L nome-dominio.it
Se tutto è andato per il verso giusto dovresti vedere qualcosa del genere:
HTTP/1.1 301 Moved Permanently
Server: nginx/1.14.0 (Ubuntu)
Date: Thu, 02 Apr 2020 13:16:28 GMT
Content-Type: text/html
Content-Length: 194
Connection: keep-alive
Location: https://nome-dominio.it/
HTTP/2 301
server: nginx/1.14.0 (Ubuntu)
date: Thu, 02 Apr 2020 13:16:29 GMT
content-type: text/html
content-length: 194
location: https://www.nome-dominio.it/
HTTP/2 200
server: nginx/1.14.0 (Ubuntu)
date: Thu, 02 Apr 2020 13:16:29 GMT
content-type: text/html
content-length: 9
last-modified: Thu, 02 Apr 2020 12:51:25 GMT
etag: "5e85dfcd-9"
accept-ranges: bytes
Possiamo vedere come la chiamata a https://nome-dominio.it/ viene rediretta su https://www.nome-dominio.it/ che infine serve la connessione sul protocollo HTTP/2.
Step 3: abilitare HTTP/2 e gli header di sicurezza
Come ciliegina sulla torta concludiamo abilitando HTTP/2, più tutta una serie di header di sicurezza per il web server.
Configurare HTTP/2
Per attivare HTTP/2 su Nginx è sufficiente (a patto di avere un certificato attivo) includere l'istruzione http2 nella direttiva listen
:
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89;
return 301 https://$host$request_uri;
}
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89:443 ssl http2;
ssl_certificate /home/nome-dominio/ssl.combined;
ssl_certificate_key /home/nome-dominio/ssl.key;
root /home/nome-dominio/public_html;
index index.html index.htm index.php;
access_log /var/log/virtualmin/nome-dominio.it_access_log;
error_log /var/log/virtualmin/nome-dominio.it_error_log;
}
Abilitare gli header di sicurezza
Per quanto riguarda invece gli header relativi alla sicurezza, possiamo testare con un tool come securityheaders.com la nostra configurazione.
Una configurazione "cruda", senza alcun header impostato ritorna qualcosa del genere:
Per aggiungere gli header utilizziamo l'istruzione add_header
di Nginx.
Ecco una configurazione di esempio dove puoi vedere i vari header di sicurezza, come Strict-Transport-Security, Referrer-Policy, Content-Security-Policy e altri.
Alcuni di questi header sono molto semplici da configurare, mentre per altri come Content-Security-Policy, deve essere posta molta attenzione per evitare di bloccare contenuti leciti.
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89;
return 301 https://$host$request_uri;
}
server {
server_name nome-dominio.it www.nome-dominio.it;
listen 192.168.80.89:443 ssl http2;
# Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload" always;
add_header Referrer-Policy "no-referrer";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' *.external-domain.com";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Feature-Policy "camera 'none'; geolocation 'none'";
if ($host = 'nome-dominio.it') {
return 301 https://www.$host$request_uri;
}
ssl_certificate /home/nome-dominio/ssl.combined;
ssl_certificate_key /home/nome-dominio/ssl.key;
root /home/nome-dominio/public_html;
index index.html index.htm index.php;
access_log /var/log/virtualmin/nome-dominio.it_access_log;
error_log /var/log/virtualmin/nome-dominio.it_error_log;
}
Una configurazione sicura che include tutti gli header attualmente disponibili (opportunamente configurati) ritorna un punteggio di A+:
È buona norma sottoporre a scansione periodica il server con questo tool, per verificare se esistono nuovi header di sicurezza da aggiungere per aumentare la sicurezza del web server.
È possibile verificare gli header anche con curl (e non è difficile immaginare un tool da riga di comando per un check automatico).
curl -I https://www.nome-dominio.it
HTTP/2 200
server: nginx/1.14.0 (Ubuntu)
date: Thu, 02 Apr 2020 16:14:47 GMT
content-type: text/html
content-length: 9
last-modified: Thu, 02 Apr 2020 12:51:25 GMT
etag: "5e85dfcd-9"
strict-transport-security: max-age=31536000; includeSubdomains; preload
referrer-policy: no-referrer
content-security-policy: default-src 'self'; script-src 'self' *.external-domain.com
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
feature-policy: camera 'none'; geolocation 'none'
accept-ranges: bytes
Grazie per aver letto, alla prossima!
Altre risorse
In alternativa alla configurazione "artigianale" di Nginx è possibile sfruttare un tool web-based per la generazione automatica della configurazione, offerto dalla community DigitalOcean: Nginx configuration generator.
Per una guida su come configurare HTTP/2 su Apache leggi Come configurare HTTP/2 su Virtualmin GPL con il repo CodeIT