HexTechie
A web for devs from 0 to F

Spring Boot With Nginx

Simple Nginx configuration for a Spring Boot application.
-- By Jan
February 11, 2022
article image

Sometimes when deploying a Spring Boot application on a server, we use another piece of software. An example of that piece of software is Nginx.

There are many reasons why someone would use Nginx together with a Spring Boot application, like:

  • simple deployment
  • serves as a reverse proxy
  • handles SSL connections
  • can be used as a load balancer
  • already handles compression
  • can serve as another layer of security with basic auth, IP allowance or denial, IP blocking with GeoIP
  • and many more

On an ubuntu server after Nginx is installed, Nginx configurations can be added to a folder /etc/nginx/sites-available. On an ubuntu server after Nginx is installed, Nginx configurations can be added to a folder /etc/nginx/sites-available. So let's create a simple configuration that will set up Nginx as a reverse proxy handling requests for a domain example.org with a Spring Boot app running on our server.

/etc/nginx/sites-available/example.org
        upstream example_org_backend {
    server localhost:8081;
    keepalive 64;
}

server {
    listen 80;
    listen [::]:80;
    server_name example.org;

    access_log /var/log/nginx/example.org.access.log;
    error_log /var/log/nginx/example.org.error.log;

    location / {
        return 301 https://example.org$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.org;

    ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;  # about 40000 sessions
    ssl_session_tickets off;
    
    ssl_dhparam /etc/nginx/dh.pem;
    
    # intermediate configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    
    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;
    
    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    
    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /etc/letsencrypt/live/example.org/chain.pem;

    # replace with the IP address of your resolver
    resolver 1.1.1.1;

    location / {
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_pass_request_headers on;
        proxy_set_header Connection "keep-alive";
        proxy_store off;
        proxy_pass http://example_org_backend;
    }
}
    

Our config file contains three main sections:

  • definition of our upstream server (i.e. our Spring Boot app)
  • server listening on port 80
  • server listening on port 443

Upstream directive

This directive specifies group of servers that will be passed to our proxy_pass directive. If we specify more than one server, client requests will be distributed among all specified servers. With more than one server, we can use Nginx as a load balancer. keepalive directive specifies how many connections should be kept alive. In our case, server represents Spring Boot app running on localhost with port 8081.

Server directive with port 80

This section sets up Nginx to listen on port 80 and for each request returns a 301 response to redirect user to the same website with the same request uri but on port 443 with SSL encryption enabled.

Server directive with port 443

This directive specifies our core logic for our Spring Boot app. Our example already contains standard paths to let's encrypt certificates in case we have it deployed on our server. Otherwise we can be change it to any other certificate we already have for our domain. SSL directives are actually used from a nice tool created by mozilla, available here.

location directive specifies, that every request should be passed to our Spring Boot app specified by our proxy_pass directive with our upstream server. There are some things to keep in mind though, a generic Spring Boot application already has implemented parsing of X-Forwarded-* headers, but needs to be enabled. You probably already use application.properties file for your Spring Boot app, so you can simply append server.forward-headers-strategy=native to it. With a value of native, Spring Boot app uses tomcat (tomcat is used by default, if not changed) to parse headers to correctly set remote address available inside our requests. There is also an option framework which sets up a new Spring Boot bean with a filter that handles X-Forwarded-* headers instead of tomcat. Without this settings, our app would show us a remote address of our request as 127.0.0.1, which is not desired.

In our configuration we can set proxy_set_header X-Forwarded-For as $proxy_add_x_forwarded_for instead of $remote_addr which appends $remote_addr to existing X-Forwarded-For header or creates a new header with that value. But since our app is directly facing public internet and not another proxy, we don't want any user to create his own requests with already set X-Forwarded-For header with a custom ip address, so instead we overwrite it with current user's ip address. But keep in mind, every X-Forwarded-* header used by our app needs to be set correctly, otherwise a user can write its own values for each header.

Final steps

Our config is done! Now we can do some final steps to apply our configuration:

        # Let's change our current directory 
cd /etc/nginx/sites-enabled

# Create a symlink to our config file
ln -s ../sites-available/example.org

# Test our configuration before applying
nginx -t

# Finally we can reload Nginx to apply our configuration
systemctl reload nginx
    

Conclusion

That's all! We created our Nginx configuration with our custom example.org domain with a Spring Boot backend app and deployed it successfully. Easy!