Skip to main content
Advertisement

Static File Split Serving

When Nginx handles static files directly and only forwards dynamic requests to Tomcat, you eliminate wasteful thread usage in Tomcat and dramatically improve overall throughput. This chapter covers extension-based routing, directory-based routing, and practical file path management.


Why Split Static File Serving?

When Tomcat handles static files:
- /static/logo.png request → consumes 1 Tomcat thread (Java I/O)
- 100 concurrent static file requests → 100 threads consumed → dynamic requests delayed

When Nginx handles static files:
- /static/logo.png request → Nginx event loop (OS sendfile)
- Tomcat threads used only for business logic
ItemNginx Direct ServingTomcat Serving
ThroughputTens of thousands/secHundreds to thousands/sec
MemoryEvent-based, minimalThread memory required
CPUOS sendfile utilizationJVM overhead
Cache-ControlEasy to configureRequires code/config

Method 1: Extension-Based Routing

upstream tomcat_backend {
server 127.0.0.1:8080;
keepalive 32;
}

server {
listen 443 ssl;
server_name example.com;
root /var/www/myapp;

# Image files — Nginx direct serving
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|bmp|tiff)$ {
expires 30d;
add_header Cache-Control "public, immutable";
add_header Vary "Accept-Encoding";
access_log off;
}

location ~* \.(css|js|mjs)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}

location ~* \.(woff|woff2|ttf|eot|otf)$ {
expires 1y;
add_header Cache-Control "public";
add_header Access-Control-Allow-Origin "*"; # Font CORS
access_log off;
}

# Remaining requests — proxy to Tomcat
location / {
proxy_pass http://tomcat_backend;
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_set_header Connection "";
}
}

Method 2: Directory-Based Routing

For Spring Boot or traditional Java EE apps where static files live at specific paths like /static/, /resources/, /public/.

server {
listen 443 ssl;
server_name example.com;
root /opt/tomcat/latest/webapps/ROOT;

# /static/ path — Nginx direct serving
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;

# 404 if file not found (don't pass to Tomcat)
try_files $uri =404;
}

# /uploads/ — user-uploaded files
location /uploads/ {
alias /var/www/uploads/;
expires 7d;
add_header Cache-Control "public";

# Security: block executable files
location ~* \.(php|jsp|py|rb|sh|exe)$ {
deny all;
}
}

location = /favicon.ico {
log_not_found off;
access_log off;
expires 1y;
}

location = /robots.txt {
log_not_found off;
access_log off;
}

# Remaining — Tomcat
location / {
proxy_pass http://127.0.0.1:8080;
include /etc/nginx/snippets/proxy-params.conf;
}
}

Method 3: External Path Serving (Outside webapps/)

Managing static files separately from Tomcat's webapps/ directory in production.

server {
listen 443 ssl;
server_name example.com;

location /assets/ {
alias /var/www/static/;
expires 1y;
add_header Cache-Control "public, immutable";
gzip_static on; # Serve .gz files if available
}

location /api/ {
proxy_pass http://127.0.0.1:8080/api/;
include /etc/nginx/snippets/proxy-params.conf;
}

location / {
proxy_pass http://127.0.0.1:8080;
include /etc/nginx/snippets/proxy-params.conf;
}
}

gzip Compression and Static File Optimization

# Global gzip (nginx.conf http block)
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml
font/woff
font/woff2;

# Pre-compressed file serving
location ~* \.(css|js)$ {
gzip_static on; # Serve /app.js.gz if available
expires 1y;
add_header Cache-Control "public, immutable";
}

Deployment Script Example

# Extract static files from WAR
VERSION=$(date +%Y-%m-%d-%H%M%S)
mkdir -p /var/www/static/$VERSION
unzip -j myapp.war "static/*" -d /var/www/static/$VERSION/

# Point symlink to current version
ln -sfn /var/www/static/$VERSION /var/www/static/current

# Nginx serves via symlink
# location /static/ {
# alias /var/www/static/current/;
# }

Production Checklist

# 1. Verify static files served by Nginx
curl -I https://example.com/static/css/app.css
# Server: nginx (NOT tomcat)

# 2. Verify cache headers
# Cache-Control: public, immutable
# Expires: (1 year from now)

# 3. Verify no static file requests in Tomcat access log
grep "\.css\|\.js\|\.png" /opt/tomcat/latest/logs/localhost_access_log*.txt
# (should return no results)

Summary

Routing MethodWhen to Use
Extension-based (`~* .(jpgcss
Directory-based (location /static/)Static file paths are clearly separated
External path (alias)Static files managed outside WAR
gzip_staticBuild-time compression + Nginx serving
Cache strategyCSS/JS: immutable 1y, images: 30d, HTML: no-cache
Advertisement