Configure Nginx for PHP (nextcloud) + Tomcat (with SSL) ☁

1. Overview

There are scenarios where you want to have a Nginx reverse proxy server with a PHP engine running next to a Tomcat Server. You will see how to configure it a way where can have best of both worlds.

Also Transport Layer Security (SSL/TLS) for http connection will be incorporated (assuming we already have the required certificates.

We assume a configuration like this:

  • Nginx (1.10.3) Reverse Proxy installed
  • Tomcat 8.5.30 installed
  • PHP-FPM (FastCGI Process Manager) for running nextcloud
  • Separate domains for PHP content (MAINDOMAIN.NET) and Tomcat (www.JSPDOMAIN.NET) content

The PHP content we are serving is the open-source cloud nextcloud that is also accessing a MariaDB database. This is only a use case example. You can also run WordPress or other PHP based sites. If you want to know how to obtain the certificates and install Nginx, PHP and Nextcloud, check out this resource.

2. Configuration

Nginx handles will handle all incoming requests on port 80 and 443 (https). Depending on the URI path and domain it will dispatch the request to the upstream PHP module or the tomcat server. Also, we want to enforce https traffic, so there will be a redirect for traffic coming in on port 80.

2.1. nginx.conf

We will leave the /etc/nginx/nginx.conf as is. However, it is worth to take a look if you want to tweak the basic configuration settings. Here we will set server_names_hash_bucket_size 64; since we might be dealing with multiple server names.

	
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

http {

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;

        # server_tokens off;

         server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

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

        ##
        # Gzip Settings
        ##

        gzip on;
        gzip_disable "msie6";

        # gzip_vary on;
        # gzip_proxied any;
        # gzip_comp_level 6;
        # gzip_buffers 16 8k;
        # gzip_http_version 1.1;
        # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}


#mail {
#       # See sample authentication script at:
#       # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
#       # auth_http localhost/auth.php;
#       # pop3_capabilities "TOP" "USER";
#       # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
#       server {
#               listen     localhost:110;
#               protocol   pop3;
#               proxy      on;
#       }
#
#       server {
#               listen     localhost:143;
#               protocol   imap;
#               proxy      on;
#       }
#}

2.2. Nginx costum configuration

The heart of the configuration is the file /etc/nginx/sites-available/default. First we create a backup and then a symbolic link /etc/nginx/sites-enables/default pointing to the default file in the other directory. Nginx will search this folder and include it in its configuration (see above: include /etc/nginx/sites-enabled/*; ).

First, there is a definition for the upstream PHP engine listening locally on port 9000. Next you can see the enforcement of https by redirecting it with 301.

There are two server definitions for port 443 traffic. Each is concert with a different server name. We assume that tomcat content is accessed by the end-user via the domain www.JSPDOMAIN.NET. Our DNS configuration redirects also from JSPDOMAIN.NET to www.JSPDOMAIN.NET. Nonetheless we define server_name for both URIs. The proxy_pass forwards all decrypted traffic to our tomcat server listening on port 8080 and our app running in MyApp-1.0-SNAPSHOT folder. In the end, it will we working in it’s root domain www.JSPDOMAIN.NET/.

Traffic coming from our other domain (MAINDOMAIN.NET) will access data in /var/www/. There are also file-ending location filters for PHP content that is passed to our engine via fastcgi_pass. Besides that, we have common rewrite rules and serving static content with expiration headers. The next cloud application is accessed via the subpath MAINDOMAIN.NET/nextcloud. It is also possible to run other apps, say WordPress in MAINDOMAIN.NET/wordpress.

upstream php-handler {
    server 127.0.0.1:9000;
}

server {
    listen 80;
    server_name MAINDOMAIN.NET;
    location / {
    	return 301 https://$server_name$request_uri; # enforce https
    }
}

server {
    listen 80;
 	server_name www.JSPDOMAIN.NET JSPDOMAIN.NET;
    location / {
    	return 301 https://www.JSPDOMAIN.NET; # enforce https
    }
}

server {
 	listen 443;
 	server_name www.JSPDOMAIN.NET JSPDOMAIN.NET;

    ssl_certificate /etc/letsencrypt/live/www.JSPDOMAIN.NET/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.JSPDOMAIN.NET/privkey.pem;

    # Path to the root of your app installation
    root /opt/apache/tomcat/webapps/MyApp-1.0-SNAPSHOT/;

    location / {
		proxy_pass http://localhost:8080/MyApp-1.0-SNAPSHOT/;
    }
}

server {
    listen 443 ssl;
    server_name MAINDOMAIN.NET;
    ssl_certificate /etc/letsencrypt/live/MAINDOMAIN.NET/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/MAINDOMAIN.NET/privkey.pem;

    # Path to the root of your installation
    root /var/www/;
    client_max_body_size 1000M; # set max upload size
    fastcgi_buffers 64 4K;
    fastcgi_read_timeout 300;
    rewrite ^nextcloud/caldav(.*)$ nextcloud/remote.php/caldav$1 redirect;
    rewrite ^nextcloud/carddav(.*)$ nextcloud/remote.php/carddav$1 redirect;
    rewrite ^nextcloud/webdav(.*)$ nextcloud/remote.php/webdav$1 redirect;
    index index.php index.html;
    error_page 403 nextcloud/core/templates/403.php;
    error_page 404 nextcloud/core/templates/404.php;

   location = nextcloud/robots.txt {
       allow all;
       log_not_found off;
       access_log off;
   }
   location ~ ^/nextcloud/(?:\.htaccess|data|config|db_structure\.xml|README) {
		deny all;
   }
   location / {
   	# The following 2 rules are only needed with webfinger
   		rewrite ^/nextcloud/.well-known/host-meta /nextcloud/public.php?service=host-meta last;
     	rewrite ^/nextcloud/.well-known/host-meta.json /nextcloud/public.php?service=host-meta-json last;
      	rewrite ^/nextcloud/.well-known/carddav /nextcloud/remote.php/carddav/ redirect;
      	rewrite ^/nextcloud/.well-known/caldav /nextcloud/remote.php/caldav/ redirect;
      	rewrite ^(/nextcloud/core/doc/[^\/]+/)$ $1/nextcloud/index.html;
 #     try_files $uri $uri/ index.php;
        autoindex on;
  }
 location ~ \.php(?:$|/) {
        #try_files $uri /$uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        if (!-f $document_root$fastcgi_script_name) {
                    return 404;
        }
        include fastcgi_params;
       # fastcgi_param SCRIPT_FILENAME /var/www/$fastcgi_script_name;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param HTTPS on;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
#       include fastcgi.conf;
   }
   # Optional: set long EXPIRES header on static assets
    location ~* \.(?:jpg|jpeg|gif|bmp|ico|png|css|js|swf)$ {
         expires 30d;
         # Optional: Don't log access to assets
         access_log off;
    }
}

2.3. Tomcat context.xml

Since we want to access our MyApp Tomcat Application in the root path of www.JSPDOMAIN.NET we define path=”” sessionCookiePath=”/” in the context tag in the tomcat apps context.xml file. This is necessary because the browser will only then set the cookie if it also sees a root path for the cookie from our app. We need cookies in tomcat for setting the JSESSIONID when working with user authentication (e.g. Apache Shiro) and so on.

You can also see a Resource definition for access to our MariaDB server locally.

	
<?xml version="1.0" encoding="UTF-8"?>
<Context path="" sessionCookiePath="/">
  <!-- maxTotal: Maximum number of database connections in pool. Make sure you
         configure your mysqld max_connections large enough to handle
         all of your db connections. Set to -1 for no limit.
         -->
  <!-- maxIdle: Maximum number of idle database connections to retain in pool.
         Set to -1 for no limit.  See also the DBCP documentation on this
         and the minEvictableIdleTimeMillis configuration parameter.
         -->
  <!-- maxWaitMillis: Maximum time to wait for a database connection to become available
         in ms, in this example 10 seconds. An Exception is thrown if
         this timeout is exceeded.  Set to -1 to wait indefinitely.
         -->
  <!-- username and password: MySQL username and password for database connections  -->
  <!-- driverClassName: Class name for the old mm.mysql JDBC driver is
         org.gjt.mm.mysql.Driver - we recommend using Connector/J though.
         Class name for the official MySQL Connector/J driver is com.mysql.jdbc.Driver.
         -->
  <!-- url: The JDBC connection url for connecting to your MySQL database.
         -->
 
  <Resource auth="Container" driverClassName="org.mariadb.jdbc.Driver" factory="org.apache.commons.dbcp2.BasicDataSourceFactory" maxIdle="30" maxTotal="100" maxWaitMillis="10000" name="jdbc/TestDB" password="PASSWORD" type="javax.sql.DataSource" url="jdbc:mariadb://localhost:3306/mydb" username="USERNAME"/>
 
</Context>

3. Conclusion

Now we have a resource sharing server configuration. Popular deployment targets for Nextcloud are on a private hardware like Raspberry Pi 3 B (+). Also the tomcat server with ssl is great if you want to avoid shared hosting services or clouds like AWS.

With Nginx and PHP-FPM we can achieve a decent performance. It is worthwile to test the access times via tools like Apache Benchmark.

This tutorial shows also how to make use of the configuration syntax of Nginx to set up complex server.

Leave a Reply

Your email address will not be published. Required fields are marked *