Node and php together with ssl certificates

March 18, 2017 134 views
Node.js PHP Nginx Ubuntu 16.04

I successfully deployed a node server with ssl certificates with nignx following this tutorial https://code.lengstorf.com/deploy-nodejs-ssl-digitalocean/ . Now i want to run a php on the main domain and change the node app to a subdomain like app.mydomain.com

My sites-enabled file looks like this at the moment

# HTTP — redirect all traffic to HTTPS
server {
    listen 80;
    listen [::]:80 default_server ipv6only=on;
    return 301 https://$host$request_uri;
}
# HTTPS — proxy all requests to the Node app
server {
    # Enable HTTP/2
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.mydomain.com;

    # Use the Let’s Encrypt certificates
    ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;

    # Include the SSL configuration from cipherli.st
    include snippets/ssl-params.conf;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://localhost:5000/;
        proxy_ssl_session_reuse off;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_redirect off;
    }
}


Is it possible to do that?

3 Answers

You could use NGINX as a load balance/proxy server with SSL termination.

https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-load-balancing-with-ssl-termination

I use this technique to issue and manage SSL traffic to the main domain with separate certificates/virtual hosts to sub.domains.

by Jesin A
Use SSL termination to reduce your SSL certificate and software management overhead on load-balanced servers. This tutorial uses an Nginx load-balancer and LAMP backends.

@tamburrinipietro89

Absolutely -- to change the current server block to a sub-domain, you'd simply modify this line:

server_name www.mydomain.com;

You'd want to change www to the sub-domain you're wanting to use, for example:

server_name mysub.mydomain.com;

or

server_name app.mydomain.com;

Once those changes are made, you'd setup a new server block for the main domain, install PHP and the packages that you require, setup the new home directory, etc.

NGINX relies on the PHP's FPM package, so you'll want to make sure that's installed when you go to install your PHP packages.

I prefer to stick with the latest version of PHP, so when it comes to deploying PHP, I'll add the repo to enable PHP 7.1.x using:

sudo add-apt-repository -y ppa:ondrej/php

Then:

apt-get update

No we can get PHP and PHP-FPM installed using:

apt-get install -y php7.1-cli php7.1-dev php7.1-fpm php7.1-bcmath php7.1-bz2 php7.1-common php7.1-curl php7.1-enchant php7.1-gd php7.1-gmp php7.1-imap php7.1-intl php7.1-json php7.1-mbstring php7.1-mysql php7.1-pspell php7.1-readline php7.1-recode php7.1-soap php7.1-sqlite3 php7.1-tidy php7.1-xml php7.1-xmlrpc php7.1-zip php7.1-opcache php7.1-xsl

Once PHP finishes installing, all that's left to do is setup the server block. To get you started, we can use a standard server block as an example to build on:

server
{
    listen 80
    listen [::]:80;
    server_name mydomain.com www.mydomain.com;

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

    location ~ [^/]\.php(/|$) {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $request_filename;
        include fastcgi_params;
    }
}

The above uses TCP instead of sockets, and to continue using TCP, you'd need to modify the existing configuration file for PHP FPM located in:

/etc/php/7.1/fpm/pool.d/www.conf

and search for:

listen =

And replace the socket with:

127.0.0.1:9000

and restart NGINX as well as PHP-FPM.

  • The server block
    server
    {
    listen 80
    listen [::]:80;
    server_name mydomain.com www.mydomain.com;

    location / {
        try_files $uri $uri/ /index.php;
    }
    
    location ~ [^/]\.php(/|$) {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $request_filename;
        include fastcgi_params;
    }
    

    }

    Must be added under th eother block right?

    • @tamburrinipietro89

      You can create new server block files per site, or you can include them all in one file. I find it easier to manage sites, especially when there's 5-10 or more, by separating them in to their own individual files.

  • I installed php and changed the listen but when i add the block and restart nginx wont restart beacause of errors. My site enable lokks like this now. But if i remove the third block everything runs (except the php server of course)

    # HTTP — redirect all traffic to HTTPS
    server {
        listen 80;
        listen [::]:80 default_server ipv6only=on;
        return 301 https://$host$request_uri;
    }
    # HTTPS — proxy all requests to the Node app
    server {
        # Enable HTTP/2
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name app.mydomain.com;
    
        # Use the Let’s Encrypt certificates
        ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;
    
        # Include the SSL configuration from cipherli.st
        include snippets/ssl-params.conf;
    
        location / {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-NginX-Proxy true;
            proxy_pass http://localhost:5000/;
            proxy_ssl_session_reuse off;
            proxy_set_header Host $http_host;
            proxy_cache_bypass $http_upgrade;
            proxy_redirect off;
        }
    }
    
    server
    {
        listen 80
        listen [::]:80;
        server_name mydomain.com www.mydomain.com;
    
        location / {
            try_files $uri $uri/ /index.php;
        }
    
        location ~ [^/]\.php(/|$) {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            include fastcgi_params;
        }
    }
    
    
    • @tamburrinipietro89

      Please run:

      tail -20 /var/logs/nginx/error.log
      

      ... and post the output. That'll help us figure out what's going on.

      The only issue I can see the with the above three blocks is in the first one. You're missing a server_name directive which means that any requests on port 80 are going to get sent to 443 regardless of whether they have SSL active or not.

      So this:

      server {
          listen 80;
          listen [::]:80 default_server ipv6only=on;
      
          return 301 https://$host$request_uri;
      }
      

      Should be modified to this (added server_name, removed default_server):

      server {
          listen 80;
          listen [::]:80 ipv6only=on;
          server_name app.mydomain.com;
      
          return 301 https://$host$request_uri;
      }
      

      The above ensures that only requests sent to app.mydomain.com get redirected to SSL. The same should be done for any sites that have SSL active.

      • Id like to have both php and node on ssl
        Now nginx restarts properly but redirects all requests to node

        # HTTP — redirect all traffic to HTTPS
        server {
            listen 80;
            listen [::]:80 default_server ipv6only=on;
            return 301 https://$host$request_uri;
        }
        # HTTPS — proxy all requests to the Node app
        server {
            # Enable HTTP/2
            listen 443 ssl http2;
            listen [::]:443 ssl http2;
            server_name app.mydomain.com;
        
            # Use the Let’s Encrypt certificates
            ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
            ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;
        
            # Include the SSL configuration from cipherli.st
            include snippets/ssl-params.conf;
        
            location / {
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-NginX-Proxy true;
                proxy_pass http://localhost:5000/;
                proxy_ssl_session_reuse off;
                proxy_set_header Host $http_host;
                proxy_cache_bypass $http_upgrade;
                proxy_redirect off;
            }
        }
        
        server
        {
               listen 443 ssl http2;
            listen [::]:443 ssl http2;
        
            server_name mydomain.com www.mydomain.com;
        
             # Use the Let’s Encrypt certificates
            ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
            ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;
        
        
            location / {
                try_files $uri $uri/ /index.php;
            }
        
            location ~ [^/]\.php(/|$) {
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $request_filename;
                include fastcgi_params;
            }
        }
        

@tamburrinipietro89

The issue, as noted in my previous response, is with this server block:

server {
    listen 80;
    listen [::]:80 default_server ipv6only=on;
    return 301 https://$host$request_uri;
}

You're not specifying a server_name, thus it redirects any request to the first server block it finds. In your case, that's your NodeJS application (this is how NGINX works by default, the first server block is going to get non-specific requests).

What you need to do is correct that server block and add another as you need two -- one for each of the domains.

For example, for the NodeJS application:

server {
    listen 80;
    listen [::]:80;
    server_name app.mydomain.com;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name app.mydomain.com;

    ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;

    include snippets/ssl-params.conf;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://localhost:5000/;
        proxy_ssl_session_reuse off;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_redirect off;
    }
}

Then for the PHP application:

server {
    listen 80;
    listen [::]:80;
    server_name mydomain.com www.mydomain.com;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name mydomain.com www.mydomain.com;

    ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;

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

    location ~ [^/]\.php(/|$) {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $request_filename;
        include fastcgi_params;
    }
}

NGINX won't allow you to have a general purpose port 80 redirect that just works on all domains, you have to set a redirect up for each domain you want to redirect requests on port 80 to 443.

Also note, that unless you setup your SSL certificate to work across:

mydomain.com
www.mydomain.com
app.mydomain.com

Then you'll need to make sure you setup individual SSL Certificates and correct the paths to the SSL files, otherwise the certificates may show as invalid.

Have another answer? Share your knowledge.