Introduzione

Quando ho costruito la nostra applicazione finanziaria su Tomcat, ho rapidamente capito che mettere Tomcat direttamente su Internet pubblico non era una buona idea. Funzionava all'inizio, ma era fragile, difficile da mettere in sicurezza e quasi impossibile da mantenere senza intoppi durante i deploy.

Alla fine, ho deciso di mettere Nginx davanti a Tomcat e lasciare che Nginx gestisse tutto ciò in cui Tomcat non è bravo, in particolare la terminazione SSL, la modalità di manutenzione, la cache dei file statici e la mascheratura dei dettagli dell'infrastruttura interna.

Questo articolo spiega la configurazione esatta che utilizzo in produzione oggi. Non è una guida generica. È scritta specificamente per il sistema finanziario che ho implementato e include configurazioni reali e le ragioni dietro ogni decisione.

Perché avevo bisogno di Nginx davanti a Tomcat

1. Una pagina di manutenzione pulita quando Tomcat si riavvia

In un sistema finanziario, spesso dobbiamo riavviare Tomcat durante i deploy o la manutenzione. Se Tomcat è inattivo, l'utente non dovrebbe mai vedere una pagina 503 grezza di Tomcat che espone informazioni sul server.

Con Nginx, gli utenti vedono sempre una pagina di manutenzione amichevole, anche se Tomcat è completamente offline. Nginx intercetta la risposta 502 o 503 e serve una pagina HTML statica che ho preparato.

2. SSL è molto più semplice su Nginx

Configurare SSL all'interno di Tomcat è inutilmente complicato. Devi convertire i certificati in un keystore Java, gestire il connettore e affrontare più formati.

Con Nginx, SSL è semplice. Ho bisogno solo di due file, un certificato e una chiave privata. Nginx termina l'HTTPS e Tomcat riceve solo HTTP semplice da localhost. Il rinnovo del certificato e il debug sono anche più facili.

3. Tomcat è completamente nascosto dal mondo esterno

Per motivi di sicurezza, non voglio che Tomcat esponga le sue porte o le informazioni sul server agli utenti pubblici. Tomcat ascolta solo su 127.0.0.1:8080.

Se qualcuno prova a scansionare il mio server, non può rilevare che Tomcat esiste.

4. Intestazioni grandi per le approvazioni finanziarie

Alcune operazioni di approvazione nel sistema inviano un lungo elenco di ID di applicazioni finanziarie nell'intestazione o nella stringa di query.

Per supportare queste intestazioni grandi, ho configurato Nginx per consentire buffer di intestazione più grandi. Senza questo, alcune richieste fallirebbero perché la dimensione predefinita dell'intestazione è troppo piccola.

5. Caricamenti di file grandi

Gli utenti caricano contratti, fatture e documenti scansionati. Ho impostato un limite di dimensione per i caricamenti pulito in Nginx in modo che i file grandi non raggiungano nemmeno Tomcat a meno che non superino prima la validazione di Nginx.

6. Contenuti statici più veloci (CSS, JS, icone)

Anche se Tomcat può servire file statici, Nginx è molto più veloce. Ho abilitato la cache per le risorse statiche in modo che Nginx le serva direttamente senza disturbare Tomcat.

7. Timeout personalizzati per operazioni a lungo termine

Alcuni processi in un sistema finanziario richiedono più tempo, come la generazione di report o l'approvazione in batch.

Nginx mi dà il pieno controllo sui timeout del proxy in modo che gli utenti non ricevano errori 504 inaspettati.

La configurazione HTTPS di Nginx che utilizzo

Tengo questo file in:

/etc/nginx/sites-available/myapp_https

Ecco la configurazione effettiva, semplificata per l'articolo ma comunque accurata.

server {
    listen 443 ssl;
    server_name finance.example.com;

    ssl_certificate     /opt/myapp/keystore/app.crt;
    ssl_certificate_key /opt/myapp/keystore/app.key;

    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;

    large_client_header_buffers 4 65k;
    client_max_body_size 100m;

    error_page 502 503 = @maintenance;
    error_page 404 = @errors;

    access_log /opt/myapp/nginx/logs/access.log;
    error_log  /opt/myapp/nginx/logs/error.log;

    location / {
        proxy_set_header Host                $host;
        proxy_set_header X-Forwarded-Host    $host;
        proxy_set_header X-Forwarded-Server  $host;
        proxy_set_header X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto   https;

        proxy_pass http://127.0.0.1:8080;

        proxy_buffering off;
        proxy_buffer_size 128k;
        proxy_buffers 100 128k;

        proxy_connect_timeout 180;
        proxy_send_timeout    180;
        proxy_read_timeout    180;
        send_timeout          180;

        location ~* \.(css|js|jpg|gif|ico)$ {
            proxy_cache cache;
            proxy_cache_key   $host$uri$is_args$args;
            proxy_cache_valid 200 301 302 30m;
            expires 30m;
            proxy_pass http://127.0.0.1:8080;
        }
    }

    location = / {
        return 301 /myapp/;
    }

    location @maintenance {
        root /opt/myapp/nginx/www;
        rewrite ^ /maintenance.html break;
    }

    location @errors {
        root /opt/myapp/nginx/www;
        rewrite ^ /404.html break;
    }
}

Questo è il cuore del sistema. Nginx gestisce SSL, inoltra il traffico a Tomcat, serve la pagina di manutenzione quando Tomcat è offline, memorizza nella cache i contenuti statici e protegge la disposizione della rete interna.

Redirect HTTP (Porta 80)

Ho aggiunto una configurazione semplice che reindirizza tutto il traffico HTTP a HTTPS.

/etc/nginx/sites-available/myapp_http
server {
    listen 80;
    server_name finance.example.com;
    return 301 https://$host$request_uri;
}

Struttura delle cartelle

Tengo i file relativi a Nginx all'interno di una cartella dedicata all'applicazione:

/opt/myapp/
    keystore/
    nginx/
        logs/
        www/

Questo rende facile gestire certificati, log e pagine statiche.

Configurazione di Tomcat che completa l'impostazione

All'interno di server.xml di Tomcat, collego Tomcat a localhost in modo che non possa essere accessibile dall'esterno.

<Connector port="8080"
           address="127.0.0.1"
           maxHttpHeaderSize="65536"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           maxThreads="150"/>

Faccio anche in modo che Tomcat comprenda il vero IP del client e il protocollo:

<Valve className="org.apache.catalina.valves.RemoteIpValve"
       remoteIpHeader="X-Forwarded-For"
       protocolHeader="X-Forwarded-Proto"
       protocolHeaderHttpsValue="https"/>

Questo assicura che il sistema registri il corretto IP del client e si comporti correttamente per HTTPS.

Perché questa configurazione funziona bene per un sistema finanziario

Mettere Nginx davanti a Tomcat ha reso l'intero sistema molto più professionale e stabile. Durante i deploy o i riavvii, gli utenti non si imbattono mai nelle tipiche pagine 503 di Tomcat. Invece, vedono sempre una schermata di manutenzione pulita, il che rende il sistema affidabile anche quando si lavora dietro le quinte.

Gestire SSL su Nginx semplifica anche tutto. I certificati sono più facili da gestire, rinnovare e debug, e Tomcat non deve più affrontare direttamente l'HTTPS. Un altro vantaggio è la sicurezza. Poiché Tomcat ascolta solo su localhost, è completamente nascosto dal traffico esterno, il che elimina un'intera categoria di tentativi di scansione e probing.

Le prestazioni migliorano anche. I file statici come CSS e JavaScript si caricano più velocemente perché Nginx può memorizzarli nella cache, e i caricamenti vengono controllati e limitati prima di raggiungere Tomcat. Alcune delle nostre operazioni finanziarie coinvolgono intestazioni grandi o azioni a lungo termine, e Nginx mi dà il pieno controllo sulle dimensioni dei buffer e sui timeout, il che rende queste operazioni fluide senza errori casuali.

Anche nei casi in cui Tomcat va offline per un momento, il sito risponde comunque in modo prevedibile grazie alla pagina di manutenzione. Negli anni, questa configurazione si è dimostrata estremamente solida. Ha aiutato l'applicazione finanziaria a raggiungere quasi zero downtime in molte release e cambiamenti di infrastruttura, e continua a gestire il traffico di produzione senza sorprese.