10 Step Guide: Varnish + Nginx + PHP On Ubuntu With SSL
Tags: devops, php, ubuntu, nginx
Published: 2021-02-15 21:03:12

---
title: 10 Step Guide: Varnish + Nginx + PHP On Ubuntu With SSL
published: true
description: How to set up Varnish on Ubuntu with Nginx hosts and SSL termination using HAProxy
tags: devops, php, ubuntu, nginx
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9tga2fw2aim4jf5wj89p.jpg
---

Setting up Varnish with SSL can be tricky, largely because there isn't any one place to get all the information you need in a straightforward manner.

Having gone through the frustration of piecing it all together myself, I decided to write a quick guide that details the process from start to finish.

Initially, I attempted the SSL portion of the job by using Hitch - which ended up being a complete waste of time (mostly because of really poor documentation). HAProxy on the other hand ended up being a great solution, as well as having other really great features like load balancing included.

Let's get straight to it. Here's the stack I'm working through in this article:

* Ubuntu 18.04
* PHP 7.4
* Nginx
* Varnish 5.2.1
* Letsencrypt SSL
* HAProxy
* Laravel 8

You can ignore the PHP and Laravel bits if you're using a different stack.

## 0. Getting Started
You will need:
* Ubuntu 18.04 installation + sudo SSH access
* DNS configured for the server block you are going to add to Nginx (it'll be example.com in this article)

## 1. Set Up Nginx
Install nginx using:
* ``sudo apt update``
* ``sudo apt install nginx``

 Set the default ports: 
* ``sudo vim /etc/nginx/sites-available/default``

```
server {
  listen 8080 default_server;
  listen [::]:8080 default_server;
  ...
}
```

**Note:** Don't create the server block for your site just yet, we'll get to that shortly.

## 2. Install PHP 7.4 + Required Extensions
If PHP isn't already installed, run the following commands to get PHP 7.4 along with extensions required for a Laravel (and most modern PHP apps) project:
* ``sudo apt install software-properties-common``
* ``sudo add-apt-repository ppa:ondrej/php``
* ``sudo apt update``
* ``sudo apt install php7.4 php7.4-cli php7.4-fpm php7.4-json php7.4-common php7.4-mysql php7.4-zip php7.4-gd php7.4-mbstring php7.4-curl php7.4-xml php7.4-pear php7.4-bcmath``

## 3. Install FPM + Composer
Install FPM and Composer if you require it:
* ``sudo apt install php7.4-fpm``
* ``sudo apt install composer``

## 4. Create Nginx server block
Now we can create the Nginx server block for our site:
* ``sudo vim /etc/nginx/sites-available/example.com``
* Paste this in (or use your preferred boilerplate):

```
server {
    listen 8080;
    server_name example.com www.example.com;
    root /var/www/example.com/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

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

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

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
```

* Save the file, then create the symlink by running: 
```sudo ls -n /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/```
* Restart Nginx: ``sudo service nginx restart``

## 5. Install Cerbot
Certbot needs to be installed to request a Let's Encrypt certificate for our site. Ensure your DNS records are pointing to this server and run the following:
* ``sudo add-apt-repository ppa:certbot/certbot``
* ``sudo apt install python-certbot-nginx``
* ``sudo certbot --nginx -d example.com -d www.example.com``
* Select **No Redirect** option
* Edit ``/etc/nginx/sites-available/example.com`` and remove SSL lines added by Certbot at the bottom of the file - it will look something like this:
```
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.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
```

## 6. Prepare PEM File
Next we will need to create a PEM file for our SSL proxy to use further down the track. To do so, we'll append the private key and fullchain key values to a single file:

Switch to root (``sudo su``), then:
```
sudo cat /etc/letsencrypt/live/example.com/privkey.pem /etc/letsencrypt/live/example.com/fullchain.pem > /etc/ssl/private/exampledotcom.pem
exit
```

## 7. Install + Configure Varnish
Varnish can now be installed and configured:
* ``sudo apt -y install varnish``
* Edit ``/lib/systemd/system/varnish.service`` and change the default port from 6081 to 80:
```
ExecStart=/usr/sbin/varnishd -j unix,user=vcache -F -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m
```
* Edit ``/etc/varnish/default.vcl`` and ensure the ``backend default`` port is set to ``8080``
* Run ``sudo systemctl daemon-reload && sudo service varnish restart`` 

Varnish should now be working for your site for HTTP.

## 8. Install + Configure HAProxy
* Install HAProxy: ``sudo apt-get install haproxy``
* Open for editing: ``sudo vim /etc/haproxy/haproxy.cfg``
* Add a front end binding, pointing towards PEM file created earlier:
```
frontend haproxynode
        bind *:443 ssl crt /etc/ssl/private/exampledotcom.pem
        mode http
        default_backend backendnodes
```
* Add back end config + node:
```
backend backendnodes
        balance roundrobin
        option forwardfor
        http-request set-header X-Forwarded-Port %[dst_port]
        http-request add-header X-Forwarded-Proto https if { ssl_fc }
        server example.com 172.1.35.35:80 check
```
**Important:** IP address above must be that of the server this host sits on. use ``ifconfig`` to determine that address.

To test the config for errors, run ``sudo haproxy -c -f /etc/haproxy/haproxy.cfg``: 

## 9. Restart Everything
* ``sudo service nginx restart && sudo service varnish restart && sudo service haproxy restart``

## 10. Trust Proxy Config (Laravel only)
* Add the server IP from step 8 to the ``$proxies`` property in ``App\Http\Middleware\TrustProxies``:
```php
protected $proxies = ['172.1.35.35'];
```

**Note:** If you are behind a load balancer where you cannot reliably determine the IP, you can do this:
```php
protected $proxies = '*';
```

## Useful Links
* Ubuntu 18.04 + Nginx install guide: https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04
* Let's Encrypt Nginx Tutorial: https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-18-04
* VCL Reference: https://varnish-cache.org/docs/5.2/users-guide/vcl.html
* An excellent VCL configuration template can be found here: https://github.com/mattiasgeniar/varnish-5.0-configuration-templates

## Thanks!
Hopefully this guide was helpful for you, thanks for taking a look. 

If there are any corrections to the above, please leave a comment and I will update the article.