Servire file statici con Nginx: consigli per l'ottimizzazione

Ottimizza la distribuzione di file statici con Nginx utilizzando intestazioni cache appropriate, compressione Gzip, impostazioni predefinite sicure e suggerimenti per proteggere i file nascosti — per siti più veloci con meno problemi di asset obsoleti.

Servire file statici con Nginx: consigli per l'ottimizzazione

Servire file statici con Nginx è uno dei modi più comuni ed efficienti per distribuire immagini, CSS, JavaScript, download e asset frontend compilati. Nginx è molto bravo in questo compito, ma alcune scelte di configurazione possono fare la differenza tra un sito veloce e prevedibile e uno che spreca larghezza di banda o serve contenuti obsoleti.

L'ottimizzazione dei file statici riguarda principalmente percorsi chiari, intestazioni cache corrette, compressione e impostazioni predefinite sicure. Non è necessaria una configurazione complicata per ottenere risultati significativi, ma è necessario che le regole cache corrispondano a come i file vengono nominati e distribuiti.

Inizia con una posizione chiara per i file statici

La configurazione più semplice per i file statici utilizza root e try_files:

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/public;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

Con questa configurazione, una richiesta per /css/app.css corrisponde a /var/www/example.com/public/css/app.css. Se il file non esiste, Nginx restituisce 404.

Questa mappatura diretta è utile durante il debug. Puoi prendere un URL dal browser, trasformarlo in un percorso del filesystem e verificare se il file esiste:

ls -l /var/www/example.com/public/css/app.css

Se il file esiste ma Nginx restituisce 404, cerca un root diverso, un blocco location più specifico o un file include che sovrascrive il percorso.

Per una single page app, potresti volere che le route sconosciute ricadano su index.html:

location / {
    try_files $uri $uri/ /index.html;
}

Questo è utile per i router frontend, ma non usarlo ciecamente per ogni sito. Se anche gli asset mancanti restituiscono index.html, il debug di percorsi JavaScript o immagini errati può diventare confuso. Molti team usano una posizione separata per gli asset in modo che i file mancanti restituiscano comunque un vero 404.

Puoi anche usare alias quando un percorso URL deve corrispondere a un percorso del filesystem diverso:

location /assets/ {
    alias /srv/shared-assets/;
}

Fai attenzione agli slash finali. Con alias, il percorso della posizione e il percorso del filesystem dovrebbero solitamente terminare entrambi con /. Una mancata corrispondenza può produrre percorsi di file imprevisti.

Un pattern sicuro è:

location /downloads/ {
    alias /srv/downloads/;
    try_files $uri =404;
}

Qui /downloads/manual.pdf corrisponde a /srv/downloads/manual.pdf. Senza la disciplina dello slash finale, è facile costruire accidentalmente percorsi che non esistono o esporre una directory che non intendevi pubblicare.

Per un approfondimento sul comportamento di corrispondenza, vedi Blocchi location di Nginx.

Aggiungi intestazioni cache del browser

I file statici sono ottimi candidati per la cache del browser. Se un utente scarica app.css una volta, il browser non dovrebbe recuperarlo di nuovo a ogni visualizzazione di pagina a meno che non sia cambiato.

Per gli asset con versione, utilizza durate cache lunghe:

location /assets/ {
    root /var/www/example.com/public;
    expires 1y;
    add_header Cache-Control "public, immutable";
}

Questo funziona meglio quando i nomi dei file cambiano durante la distribuzione, come app.8f3a91.css o bundle.20260523.js. Se il nome del file cambia quando il contenuto cambia, i browser possono memorizzare nella cache il vecchio file in modo sicuro per molto tempo.

Per i file che mantengono lo stesso nome, utilizza una cache più breve:

location = /index.html {
    root /var/www/example.com/public;
    expires -1;
    add_header Cache-Control "no-cache";
}

Questo pattern è comune per le app frontend. Il file HTML rimane fresco, mentre i file CSS e JavaScript con hash vengono memorizzati nella cache in modo aggressivo.

Un esempio pratico: la tua app React o Vue produce asset con hash in /assets/ e un semplice index.html. Metti in cache /assets/ per un anno, ma fai in modo che index.html venga riconvalidato. Gli utenti ottengono visite ripetute veloci e le nuove distribuzioni caricano comunque i riferimenti agli asset più recenti.

Dopo aver modificato le regole cache, testa le intestazioni invece di indovinare:

curl -I https://example.com/assets/app.8f3a91.css
curl -I https://example.com/

Vuoi che l'asset con hash mostri un valore Cache-Control di lunga durata. Di solito vuoi che la pagina HTML venga riconvalidata o utilizzi una durata breve. Se entrambi sono memorizzati nella cache per un anno, una distribuzione può lasciare gli utenti bloccati su un vecchio file HTML che punta a vecchio JavaScript.

Usa la compressione per gli asset di testo

Gli asset di testo come CSS, JavaScript, SVG e JSON si comprimono bene. Puoi abilitare Gzip nel blocco http:

gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_types
    text/css
    application/javascript
    application/json
    image/svg+xml;

Non aspettarti che Gzip aiuti molto con file JPEG, PNG, WebP, MP4 o zip. Questi formati sono già compressi. Tentare di comprimerli di nuovo di solito spreca CPU.

Per siti statici ad alto traffico, considera i file precompressi. Il tuo processo di build può creare versioni .gz di file CSS e JavaScript di grandi dimensioni, e Nginx può servirli quando il browser supporta Gzip:

gzip_static on;

Questo riduce il lavoro di compressione in fase di esecuzione perché Nginx legge il file precompresso dal disco. È più utile quando gli asset vengono compilati in anticipo e non cambiano tra le richieste.

La compressione è solo una parte della distribuzione degli asset. La dimensione del file conta ancora. Rimuovi JavaScript inutilizzato, ottimizza le immagini durante il processo di build ed evita di inviare file di grandi dimensioni che gli utenti non necessitano.

Quando testi la compressione, includi un'intestazione Accept-Encoding:

curl -I -H 'Accept-Encoding: gzip' https://example.com/assets/app.js

Cerca Content-Encoding: gzip e Vary: Accept-Encoding. L'intestazione Vary è importante quando un CDN o una cache condivisa si trova davanti a Nginx, perché le risposte compresse e non compresse non devono essere mescolate.

Migliora la distribuzione e la sicurezza dei file

Nginx può servire file statici in modo efficiente con le impostazioni predefinite, ma alcuni dettagli aiutano in produzione.

Primo, disabilita gli elenchi di directory a meno che non siano esplicitamente necessari:

autoindex off;

Gli elenchi di directory possono rivelare nomi di file e struttura che non intendevi pubblicare.

Secondo, blocca l'accesso ai file nascosti come .env, .git e altri file con punto:

location ~ /\.(?!well-known) {
    deny all;
}

L'eccezione per .well-known è comune perché la convalida del certificato e i file basati su standard possono utilizzare quella directory.

Terzo, assicurati che i permessi dei file statici consentano a Nginx di leggere i file ma non diano al server web accesso in scrittura non necessario. Una configurazione tipica consente agli strumenti di distribuzione di scrivere file e all'utente worker di Nginx di leggerli.

Quarto, controlla i tipi MIME. Nginx di solito include un file mime.types, ma contenitori ridotti o build personalizzate potrebbero non averlo. Se il CSS viene servito come text/plain, i browser potrebbero rifiutarlo o comportarsi diversamente.

Usa:

include /etc/nginx/mime.types;
default_type application/octet-stream;

Infine, controlla i log per risposte 404 ripetute sugli asset. Questo spesso significa che una distribuzione ha fatto riferimento a file che non esistono, una cache punta ancora a un vecchio nome file o un percorso alias è sbagliato.

Se la distribuzione statica sembra lenta, non iniziare copiando ogni direttiva di ottimizzazione che trovi. Prima verifica se il problema è effettivamente Nginx. Immagini grandi, bundle frontend non ottimizzati, mount di storage remoto e cache miss del CDN sono cause più comuni di una micro-ottimizzazione mancante nel blocco server.

Per un rapido controllo locale:

curl -o /dev/null -s -w 'status=%{http_code} size=%{size_download} time=%{time_total}\n' https://example.com/assets/app.js

Poi confrontalo con i log del CDN, gli strumenti di sviluppo del browser o una richiesta dalla stessa regione dei tuoi utenti. Una risposta veloce da Nginx e una risposta lenta nel browser di solito indicano un'altra parte del percorso di distribuzione.

Quando chiedere aiuto

Coinvolgi un ingegnere DevOps se i tuoi file statici vengono serviti da storage condiviso, volumi montati, gateway di object storage o un CDN davanti a Nginx. La migliore strategia cache dipende dall'intero percorso di distribuzione, non solo dal blocco server Nginx.

Dovresti anche chiedere aiuto se gli utenti segnalano JavaScript obsoleto dopo le distribuzioni. Questo di solito significa che le regole cache e la strategia di versionamento dei nomi file non corrispondono.

Servire file statici con Nginx funziona meglio quando i percorsi sono prevedibili, le durate cache corrispondono alla strategia dei nomi file e gli asset di testo sono compressi. Mantieni visibili i file mancanti, proteggi i file nascosti e testa le intestazioni dopo ogni modifica alla configurazione. Una configurazione statica pulita rende il tuo sito più veloce senza aggiungere parti mobili.