Ubuntu Yii2 web server with Nginx+Apache

Yii2 Ubuntu
Yii2 Ubuntu

Introduction

Nginx and Apache are two popular PHP web servers. Running both with Nginx in front of Apache (Nginx reverse proxy) will improve performance significantly. This tutorial will show configuring your Ubuntu server for Yii2 framework environments.

Prerequisites

  • Ubuntu Server 16.04 LTS "xenial"
  • Root account

Step-by-step Guide

  1. Update your OS and install the basic program

    apt-get update
    apt-get upgrade
    apt-get install unzip
    add-apt-repository -y ppa:ondrej/php
    apt-add-repository ppa:ondrej/apache2
    add-apt-repository ppa:nginx/stable
    
  2. Install Apache and PHP

    apt-get install apache2 libapache2-mod-fastcgi php7.1-fpm
    
  3. Install Apache modules

    Install mod_aclr2:

    apt-get install apache2-dev gcc
    wget http://files.directadmin.com/services/custombuild/mod_aclr2-1.0.0.tar.gz
    tar -xzvf mod_aclr2-1.0.0.tar.gz
    apxs -c mod_aclr2.c
    apxs -i -a -n aclr mod_aclr2.la
    

    Install mod_rpaf:

    wget https://github.com/gnif/mod_rpaf/archive/stable.zip
    unzip stable.zip
    cd mod_rpaf-stable
    make
    make install
    
  4. Install PHP modules (for Yii2)

    apt-get install -y php-intl php-xml php-mysql php-gd php-zip php-mcrypt php-gmp php-imagick
    apt-get install -y php-mbstring
    apt-get install -y php-mongodb
    apt-get install -y libssh2-1 php-ssh2
    
  5. Config Apache

    Create mod_aclr.conf configuration file:

    nano /etc/apache2/mods-available/aclr.conf
    

    Add the following lines:

    AccelRedirectSet On
    AccelRedirectSize 1k
    

    Create rpaf.load file for loading the module:

    nano /etc/apache2/mods-available/rpaf.load
    

    Then add the following line:

    LoadModule rpaf_module /usr/lib/apache2/modules/mod_rpaf.so
    

    Create rpaf.conf configuration file:

    nano /etc/apache2/mods-available/rpaf.conf
    

    Add the following code block, replace SERVER_IP with your server's public/private IP:

    <IfModule mod_rpaf.c>
        RPAF_Enable       On
        RPAF_Header       X-Real-Ip
        RPAF_ProxyIPs     SERVER_IP
        RPAF_SetHostName  On
        RPAF_SetHTTPS     On
        RPAF_SetPort      On
    </IfModule>
    

    Enable Apache's modules:

    a2enmod aclr
    a2enmod rpaf
    a2enmod rewrite
    a2enmod headers
    a2enmod ssl
    a2enmod proxy
    a2enmod proxy_fcgi
    a2enmod actions
    a2enmod proxy_fcgi setenvif
    a2enconf php7.1-fpm
    
  6. Changing Apache ports

    nano /etc/apache2/ports.conf
    

    Change Listen 80 to Listen 8080 and Listen 443 to Listen 8081.

    nano /etc/apache2/sites-available/000-default.conf
    

    Change <VirtualHost *:80> to <VirtualHost *:8080>.

    nano /etc/apache2/sites-available/default-ssl.conf
    

    Change <VirtualHost _default_:443> to <VirtualHost _default_:8081>.

    Open /etc/apache2/apache2.conf, add the following code block right under <Directory /var/www/> code block:

    <Directory /home/>
         Options Indexes FollowSymLinks
         AllowOverride None
         Require all granted
    </Directory>
    

    Finally, enable the default SSL site and reload Apache:

    a2ensite default-ssl
    service apache2 reload
    
  7. Install Nginx

    apt-get install nginx
    
  8. Config Nginx

    nano /etc/nginx/sites-available/default
    

    Delete all content of the file. We will redirect all HTTP requests to HTTPS, add the code block below:

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

    Next, add the code block for HTTPS:

    server {
         listen 443 default_server http2;
         listen [::]:443 default_server http2;
    
         ssl on;
         ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
         ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; 
    
         root /var/www/html;
         index index.php index.html index.htm index.nginx-debian.html;
         server_name _;
    
         location / {
              proxy_pass https://{IP}:8081;
              proxy_buffering off;
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Accel-Internal /nginx_static_files;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header X-Forwarded-Proto $scheme;
         }
    
         location /nginx_static_files/ {
              alias /var/www/html/;
              internal;
         }
    
         location ~ /(\.ht|\.user\.ini) {
              deny all;
         }
    }
    

    Remove Nginx version in the response header field:

    nano /etc/nginx/nginx.conf
    

    Uncomment server_tokens off;.

    Reload everything:

    service nginx reload
    service apache2 reload
    service php7.0-fpm reload
    

    Visit http://SERVER_IP and https://SERVER_IP to test it; then, you can block ports 8080 and 8081 using a firewall.

    Restore original visitor IP from Cloudflare: create a new file in /etc/nginx/conf.d/cloudflare.conf with the following lines:

    set_real_ip_from 103.21.244.0/22;
    set_real_ip_from 103.22.200.0/22;
    set_real_ip_from 103.31.4.0/22;
    set_real_ip_from 104.16.0.0/12;
    set_real_ip_from 108.162.192.0/18;
    set_real_ip_from 131.0.72.0/22;
    set_real_ip_from 141.101.64.0/18;
    set_real_ip_from 162.158.0.0/15;
    set_real_ip_from 172.64.0.0/13;
    set_real_ip_from 173.245.48.0/20;
    set_real_ip_from 188.114.96.0/20;
    set_real_ip_from 190.93.240.0/20;
    set_real_ip_from 197.234.240.0/22;
    set_real_ip_from 198.41.128.0/17;
    set_real_ip_from 199.27.128.0/21;
    set_real_ip_from 2400:cb00::/32;
    set_real_ip_from 2606:4700::/32;
    set_real_ip_from 2803:f800::/32;
    set_real_ip_from 2405:b500::/32;
    set_real_ip_from 2405:8100::/32;
    set_real_ip_from 2c0f:f248::/32;
    set_real_ip_from 2a06:98c0::/29;
    
    # use any of the following two
    real_ip_header CF-Connecting-IP;
    #real_ip_header X-Forwarded-For;
    
  9. Install database

    We use MariaDB instead of MySQL:

    apt-get install software-properties-common
    apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8
    add-apt-repository 'deb [arch=amd64,i386,ppc64el] http://ftp.yz.yamagata-u.ac.jp/pub/dbms/mariadb/repo/10.2/ubuntu xenial main'
    apt update
    apt install mariadb-server
    
  10. Secure web server with Let's Encrypt

    Install Certbot:

    add-apt-repository ppa:certbot/certbot
    apt-get update
    apt-get install certbot
    

    Add a new configuration file:

    nano /etc/apache2/conf-available/letsencrypt.conf
    

    Add the following line:

    Alias /.well-known /var/www/html/.well-known
    

    Enable it and reload Apache:

    a2enconf letsencrypt
    service apache2 reload
    

    Install certificate for server's hostname, note that replace {HOSTNAME} with your server hostname:

    certbot certonly --webroot --webroot-path=/var/www/html -d {HOSTNAME}
    

    Update Apache default SSL site:

    nano /etc/apache2/sites-available/default-ssl.conf
    

    Comment out SSLCertificateFile and SSLCertificateKeyFile, then add this below SSLEngine on:

    SSLCertificateFile /etc/letsencrypt/live/{HOSTNAME}/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/{HOSTNAME}/privkey.pem
    SSLCACertificateFile /etc/letsencrypt/live/{HOSTNAME}/chain.pem
    

    Next, update Nginx default site:

    nano /etc/nginx/sites-available/default
    

    Comment out ssl_certificate and ssl_certificate_key, then add this below ssl on:

    ssl_certificate /etc/letsencrypt/live/{HOSTNAME}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/{HOSTNAME}/privkey.pem;
    

    Generate Strong Diffie-Hellman Group:

    openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
    

    Add new configuration snippet:

    nano /etc/nginx/snippets/ssl-params.conf
    

    Add this content:

    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_ecdh_curve secp384r1;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=300s;
    resolver_timeout 5s;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    

    Update Nginx conf file:

    nano /etc/nginx/nginx.conf
    

    Comment out ssl_protocols and ssl_prefer_server_ciphers, then add this line right after that:

    include /etc/nginx/snippets/ssl-params.conf;
    

    Reload services:

    systemctl reload {nginx,apache2}
    

    Set Up Auto Renewal:

    crontab -e
    

    Add this line at the end:

    15 3 * * * /usr/bin/certbot renew --quiet --renew-hook "/bin/systemctl reload {nginx,apache2}"
    
  11. Enable firewall

    ufw default deny incoming
    ufw default allow outgoing
    
    ufw allow ssh
    ufw allow 80/tcp
    ufw allow 443/tcp
    
    sudo ufw disable
    sudo ufw enable
    

Conclusion

Your PHP Yii2 server is now configured with an Nginx reverse proxy.