3

I posted this on stackoverflow by mistake (https://stackoverflow.com/questions/65942820/nginx-proxy-to-tomcat) and I'm putting it here as well in hope of finding some solution.

I went through dozens of tutorials and I can't figure out following (althougt it should be pretty basic):

I have my compiled vue application in /var/www/mydomain.com and I want it to be shared as static content.

My backend running on 8080 by tomcat with public APIs on /api/something... URLs. The URLs are hardcoded including the "api" part.

I'd like to configure nginx to proxy mydomain.com/api/something... requests to tomcat and rest be served statically from /var/www/mydomain.com. Everything served through SSL.

I litterally don't need anything else.

Can you help me configure the nginx and tomcat to achieve that? Thank you!

nginx config /etc/nginx/sites-available/mydomain.com

upstream tomcat {
    server 127.0.0.1:8080 fail_timeout=0;
}

server {
        listen 443 ssl default_server;
        #listen [::]:443 ssl default_server;

        root /var/www/mydomain.com;
        index index.html index.htm index.nginx-debian.html;

       server_name _ mydomain.com www.mydomain.com;

        location /api/ {
                include proxy_params;
                proxy_set_header Host $server_name;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass http://tomcat;
        }

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

        ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem; # managed by Certbot
}
server {
    if ($host = www.mydomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = mydomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


       listen 80 default_server;
        listen [::]:80 default_server;

       server_name _ mydomain.com www.mydomain.com;
   return 404; # managed by Certbot
}

(1) Alternative location block I'm experimenting with

        location /api/ {
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-Proto https;
                proxy_pass http://localhost:8080/api/;
        }

(2) Alternative block suggested by Praveen Premaratne.

This way I get "GET /api/docs HTTP/1.0" 302 - and static files work as well. Going to /api/docs makes redirect to domain:8443/api/docs where I get ERR_CONNECTION_REFUSED.

        location /api/ {
                include proxy_params;
                proxy_pass http://tomcat;
        }

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

(3) Alternative using subdomain.

I was able to create subdomain api.mydomain.com and configure nginx to go to index page from there (adding following block). No idea how to do the proxing afterwards.

server {
        listen 443 ssl;

        root /var/www/www.mydomain.com; <- redundand I guess?
        index index.html index.htm index.nginx-debian.html; <- redundand I guess?

        server_name api.mydomain.com

        ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem; # managed by Certbot
}

Tomcat config server.xml

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               address="127.0.0.1"
               redirectPort="8443" />
<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"
         requestAttributesEnabled="true"
         pattern="%h %l %u %t &quot;%r&quot; %s %b" />
        
      <Valve className="org.apache.catalina.valves.RemoteIpValve"
         protocolHeader="X-Forwarded-Proto" /> 
   ...    

Current situation is that when I go to mydomain.com/api/docs where swagger should be running, I get redirected back to mydomain.com or get 500 or 502 error.

11
  • Try switching the order of the location block? so the /api/ comes first. I'd personally recommend run the API on a subdomain like api.mydomain.com so you don't have this problem altogether. Jan 29 at 12:53
  • I will try that and let you know. Also trying switching it for block I've just put in description. I'm completely new in deployment to production so I have no idea how would I create sudomain and what are pros and cons of it. It sounds a bit more complicated for the simple case I need. Jan 29 at 13:01
  • ok, I've added the result to the main body as (2) Alternative block Jan 29 at 13:30
  • So Nginx now does what you want but now it's hitting the Tomcat config file and that redirects the request to 8443. Does tomcat serve on 8443? and if you were to access Tomcat directly over 8080 does it redirect to 8443 as well? Jan 29 at 14:17
  • 1
    The fact that you are redirected to example.com:8443 is a crucial information (it was missing in your question on SO). Tomcat does not know that the user is connecting in a secure way. Follow this answer to stop this redirect. Jan 29 at 14:30
1

Ok, so with help of @Praveen Premaratne and @Piotr P. Karwasz and this article I came up with following configuration:

don't put in lines with "# managed by Certbot", those are created by certbot, check https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-18-04

etc/nginx/sites-available/mydomain.com

server {
    server_name    mydomain.com www.mydomain.com;

    root /var/www/mydomain.com;
    index index.html;

    access_log /var/log/nginx/mydomain-access.log;
    error_log /var/log/nginx/mydomain-error.log;

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

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = www.mydomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = mydomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    server_name    mydomain.com www.mydomain.com;
    listen 80;
    return 404; # managed by Certbot
}

/etc/nginx/sites-available/api.mydomain.com

server {
    server_name    api.mydomain.com;

    access_log /var/log/nginx/api-mydomain-access.log;
    error_log /var/log/nginx/api-mydomain-error.log;

    location / {
        proxy_set_header Host $http_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-Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:8080;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = api.mydomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    server_name    api.mydomain.com;
    listen 80;
    return 404; # managed by Certbot
}

Tomcat server.xml

<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
    connectionTimeout="20000"
    address="127.0.0.1"
    proxyName="api.mydomain.com"
    proxyPort="80"/>

<Engine name="Catalina" defaultHost="localhost">

    <Realm className="org.apache.catalina.realm.LockOutRealm">
    ...
    </Realm>

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

        <Valve className="org.apache.catalina.valves.RemoteIpValve"
            remoteIpHeader="x-forwarded-for"
            proxiesHeader="x-forwarded-by"
            protocolHeader="x-forwarded-proto" />

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

  </Host>
</Engine>
3
  • 1
    I would advise against using a catch-all regexp for internalProxies: it basically mean that an attacker can make Tomcat believe a connection is coming from any IP address it chooses. Tomcat is bound to 127.0.0.1, so it is not a problem now, but it could become in the future. Jan 31 at 19:45
  • So should I change it to internalProxies="127.0.0.1"? Feb 1 at 15:12
  • 1
    Either that or just remove the attribute and keep the default value, which seems reasonable. Feb 1 at 15:36
0

Try this:

location / {
        try_files $uri @backend;
}
location @backend {
        include proxy_params;
        proxy_pass http://tomcat;
}
3
  • Thank you. With this when I go to /api/docs, I get redirected to mydomain:8443/api/docs and there I get ERR_CONNECTION_REFUSED. Shouldn't the redirection be done somehow internally, without exposing 8443? Jan 29 at 13:10
  • But a possive thing is I get record in tomcat access log this way: GET /api/docs HTTP/1.0" 302 - Jan 29 at 13:14
  • Aaaand negative thing is this way my static files stop working... Everything gets redirected to 8443, not just /api urls. Jan 29 at 13:20
0

If I were to do this using the subdomains approach here's how I would do it.

  • Create an Nginx configuration file for the backend API
  • Create an Nginx configuration file for the static web content

Static HTML Nginx file mydomain.com.nginx

server {
    server_name    mydomain.com;

    root /var/www/mydomain.com;
    index index.html;

    access_log /var/log/nginx/mydomain-access.log;
    error_log /var/log/nginx/mydomain-error.log;

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

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = mydomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen         80;
    server_name    mydomain.com;
    return 404; # managed by Certbot


}

API Nginx config file api.mydomain.com.nginx

server {
    server_name    api.mydomain.com;

    access_log /var/log/nginx/api-mydomain-access.log;
    error_log /var/log/nginx/api-mydomain-error.log;

    location / {
        proxy_set_header X-Forwarded-Host $host:$server_port;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://127.0.0.1:8080;
        proxy_redirect off;
    }
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/api.mydomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/api.mydomain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = api.mydomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen         80;
    server_name    app.mydomain.com;
    return 404; # managed by Certbot


}

You can add these to the /etc/nginx/site-available/ directory and enable them.

Ps: I would remove the SSL stuff and run Certbot to update them since you've to issue a new certificate for the app.mydomain.com, so it would just update the files itself

7

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.