Deploying TLS Termination via Nginx Reverse Proxy for Apache Tomcat

Environment and Nginx Installation

Assume a CentOS 7 environment targeting Nginx 1.18+. Begin by configuring the official package repository and installing the binary. Ensure the http_ssl_module is compiled into the binary, as it is mandatory for TLS termination.

# Install repository management utility
yum install -y yum-utils

# Create repository definition
cat > /etc/yum.repos.d/nginx.repo <<'EOF'
[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF

# Refresh package metadata and deploy
yum makecache
yum install -y nginx

# Verify compilation flags
nginx -V 2>&1 | grep --color=auto http_ssl_module

Nginx TLS and Proxy Configuration

Place the generated certificate and private key into a dedicated directory, such as /etc/nginx/certs/. The files should be named server.crt and server.key. Avoid using the deprecated ssl on; directive. Instead, declare TLS directly within the listen instruction.

Create a dedicated configuration file at /etc/nginx/conf.d/tls-proxy.conf:

upstream tomcat_pool {
    server 127.0.0.1:8080;
    keepalive 64;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 443 ssl http2;
    server_name webapp.example.com;

    ssl_certificate     /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    location / {
        proxy_pass http://tomcat_pool;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_connect_timeout 300s;
        proxy_send_timeout 240s;
        proxy_read_timeout 240s;
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
    }

    location /ws/ {
        proxy_pass http://tomcat_pool;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

The map directive cleanly handles WebSocket connection upgrades without duplicating header logic. Buffer settings are explicit defined to prevent backend timeout errors during large payload transfers.

Tomcat Backend Adaptation

Tomcat must be informed that it is operating behind a secure proxy. Modify the conf/server.xml file to adjust the HTTP connector and inject request attributes.

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="443"
           proxyPort="443"
           proxyName="webapp.example.com"
           maxThreads="300"
           minSpareThreads="25"
           maxIdleTime="30000"
           keepAliveTimeout="15000"
           maxKeepAliveRequests="100"
           URIEncoding="UTF-8"
           maxPostSize="52428800" />

<Engine name="Catalina" defaultHost="localhost">
    <Host name="localhost" appBase="webapps"
          unpackWARs="true" autoDeploy="true">

        <Valve className="org.apache.catalina.valves.AccessLogValve"
               directory="logs" prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

        <Valve className="org.apache.catalina.valves.RemoteIpValve"
               internalProxies="127\.0\.0\.1"
               remoteIpHeader="x-forwarded-for"
               protocolHeader="x-forwarded-proto"
               requestAttributesEnabled="true" />
    </Host>
</Engine>

The RemoteIpValve translates proxy headers into standard Tomcat request attributes, ensuring the application correctly identifies the original client IP and the HTTPS protocol scheme. The proxyName and proxyPort attributes prevent redirect loops when the backend generates absolute URLs.

Service Activation and Validation

Apply the configuration changes and initialize the services:

# Validate Nginx syntax before reloading
nginx -t && systemctl restart nginx

# Launch Tomcat
/opt/tomcat/bin/startup.sh

# Verify process status
systemctl status nginx
ps aux | grep '[t]omcat'

If connectivity issues arise, inspect network filtering and mandatory access controls:

# Open HTTPS and HTTP ports permanently
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-service=http
firewall-cmd --reload

# Allow Nginx to initiate outbound network connections under SELinux
setsebool -P httpd_can_network_connect 1

# Test backend reachability
nc -vz 127.0.0.1 8080

# Verify TLS handshake and proxy routing
curl -Iv https://webapp.example.com

Tags: nginx Tomcat ssl HTTPS reverse-proxy

Posted on Thu, 06 Aug 2026 16:53:03 +0000 by zenon