UPDATE June 25th, 2014: Screw this entire post.  The guys at rtCamp (see comments) found this post and turned me on to their wonderful EasyEngine (@easyengine), which installs the entire LEMP stack and WordPress almost completely hands-free.  It also looks like a more secure installation than what I’ve advised.  So go with EasyEngine, and forget the below.  You won’t be sorry.

UPDATE June 27th, 2014: I’ve published this post, which supplants the below.  It details the EasyEngine way of setting up the LEMP WordPress stack, along with selective SSL.  Go there instead.


As I’ve said in a previous post, I’ve recently migrated this blog from a hosting provider to the AWS EC2 cloud.  On a free-tier micro instance, I was able to install the software I wanted, configure everything as I liked, and it still runs faster than my old provider.

However, installing the stack to get it the way I wanted took some time, because there was no one blog post anywhere that covered what I was trying to do.  I had to synthesize instructions for installing LEMP, installing WordPress over Nginx, and installing SSL with Nginx/WordPress.  Some of it was trial and error, too.

So dear reader, here is what I did to set up my configuration.  My configuration goals were:

  • OS: Ubuntu 14.04 LTS Server, which is the latest and greatest Ubuntu.
  • Webserver: Nginx (pronounced “Engine-Ex”), since it runs so much faster than Apache.
  • Database: MariaDB, instead of mySQL, for reasons I’ve given in my previous post.
  • PHP: to run WordPress.
  • Blogging software: WordPress: since that’s what my existing site was using.
  • SSL: for all requests for the login page and the wp-admin interface, SSL should be forced.  For all other pages, plain HTTP should be forced.

Note that I’ll cover both creation of a new  site (section III A), and migration of an existing site (section III B).

I.  ASSUMPTIONS

This document assumes that you already have the following in place, and I will not cover them:

  • Ubuntu installed on either a dedicated server, a VPS, or on AWS EC2.
  • A sudo user set up on the Ubuntu instance.
  • A purchased SSL certificate for your website.
  • You understand basic Linux commands such as cp.
  • You can use a text editor, such as vi, vim, or nano to edit files on the server.

If anyone really needs help with any the above, please leave a comment and I’ll consider adding it.

II.  INSTALL LEMP STACK

This section is mostly lifted from Digital Ocean’s post, as well as kengggg’s gist which covers MariaDB.

  1. Log in to your machine (either from the console or through ssh).
  2. Make sure that you update the package list in apt:
    sudo apt-get update
  3. Install MariaDB:
    sudo apt-get install mariadb-server
    

    Follow the prompts.
    NOTE: Do set a root user password.

  4. Set up system tables in MariaDB:
    sudo mysql_install_db

    NOTE: check the output message to make sure that this command didn’t fail.  If it did fail, remove the Aria log control file and try again:

    sudo rm /var/lib/mysql/aria_log_control
    sudo mysql_install_db
  5. Harden up security on the database:
    sudo mysql_secure_installation

    Answer “No” to “Change the root password?”, since you’ve already set it above.  Answer “Yes” to each of the remaining questions.

  6. Install and start the Nginx web server:
    sudo apt-get install nginx
    sudo service nginx start

    To test that Nginx is working, just point a browser to http://<your server’s IP address>.  You should see the “Welcome to nginx!” page.

  7. Install PHP:
    sudo apt-get install php5-fpm php5-mysql
  8. Open php.ini in your favorite text editor (I like vim):
    sudo vim /etc/php5/fpm/php.ini

    Find “;cgi.fix_pathinfo=1”.  Uncomment it (i.e remove the semi-colon), and set 1 to 0:

    cgi.fix_pathinfo=0

    This change fixes a security risk by disabling it.  If cgi.fix_pathinfo=1, then PHP will try to resolve URLs that it can’t find by returning a file that is as near to that URL as possible.  By setting it explicitly to 0, requests for incorrect URLs just fail with a 404 error.

  9. Save the file and exit the text editor.
  10. Open www.conf in your favorite text editor:
    sudo vim /etc/php5/fpm/pool.d/www.conf
  11. Find “listen = 127.0.0.1: 9000” and change it to:
    listen = /var/run/php5-fpm.sock

    NOTE: The “listen” line may already be correctly set to /var/run/php5-fpm.sock in your version of www.conf.

  12. UPDATE: The following is necessary following a security patch.  Find and uncomment the following lines (that is, remove the semi-colons), or you’ll get 502 errors from nginx in your browser:
    listen.owner = www-data
    listen.group = www-data
  13. Save the file and exit the text editor.
  14. Open the “default” config file for Nginx:
    sudo vim /etc/nginx/sites-enabled/default
  15. Add “index.php” to the “index” line:
    index index.php index.html index.htm;
  16. Uncomment the PHP section, so that it looks like the following:
    location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    #       # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
    #
    #       # With php5-cgi alone:
    #       fastcgi_pass 127.0.0.1:9000;
    #       # With php5-fpm:
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    }

    NOTE that some lines, like “fastcgi_pass 127.0.0.1:9000;” stay commented out (or you can delete them, of course).

  17. Save the file and exit the text editor.
  18. Restart PHP and Nginx:
    sudo service php5-fpm restart
    sudo service nginx restart
  19. To make sure that PHP is working correctly:
      1. create the file “info.php” in your root directory (usually /usr/share/nginx/html, but if you’re using an older version of Ubuntu, it might be /usr/share/nginx/www).
        sudo vim /usr/share/nginx/html/info.php
      2. Add the following code to the file:
        <?php
            phpinfo();
        ?>
        
      3. Save the file and exit the text editor.
      4. Browse to http://<your server’s URL>/info.php. You should see the following info page there:info.php
      5. Now that you’re sure that PHP is working, DELETE info.php.  You don’t want to advertise to a potential hacker what your server’s settings are.
        sudo rm /usr/share/nginx/html/info.php

III.  INSTALL WORDPRESS

A. FRESH INSTALL

This section assumes that you are installing WordPress from scratch, not migrating an existing site.  If you want to migrate an existing site, go to section B.

  1.  Download and unzip WordPress.
    wget http://wordpress.org/latest.tar.gz
    tar -zxvf  latest.tar.gz
  2. Create the WordPress database and user:
    1. Log into MariaDB as root:
      mysql -u root -p
    2. Create the WordPress database (called “wordpressdb” here, but call it whatever you want):
      CREATE DATABASE wordpressdb;
    3. Create the user for WordPress (again, call the user whatever you like):
      CREATE USER wordpressuser@localhost;
    4. Set password for this user:
      SET PASSWORD for wordpressuser@localhost = PASSWORD("yourpassword");
    5. Give wordpressuser full access to wordpressdb:
      GRANT ALL PRIVILEGES ON wordpressdb.* to wordpressuser@localhost 
      IDENTIFIED BY 'yourpassword';
    6. Refresh privileges:
      FLUSH PRIVILEGES;
    7. Exit the MariaDB shell:
      exit
  3. Configure WordPress to work with the new database.
    1. Copy sample config file to use as starting point for WordPress configuration.  From the directory where you downloaded and unzipped WordPress to:
      cp wordpress/wp-config-sample.php wordpress/wp-config.php
    2. Open wp-config.php in your favorite editor (like vim):
      vim wordpress/wp-config.php
    3. Find the DB_NAME, DB_USER, and DB_PASSWORD definitions and change them to the database settings that you set above:
      // ** MySQL settings - You can get this info from your web host ** //
      /** The name of the database for WordPress */
      define('DB_NAME', 'wordpressdb');
      
      /** MySQL database username */
      define('DB_USER', 'wordpressuser');
      
      /** MySQL database password */
      define('DB_PASSWORD', 'yourpassword');
      
    4. Save the file and exit the text editor.
  4. Copy the WordPress PHP files to the root directory of your website (the following assumes Nginx root directory from later versions of Ubuntu; check your version):
    sudo cp -r wordpress/* /usr/share/nginx/html/
  5. Adjust permission ownership on the WordPress files so that Nginx can access them:
    sudo chown -R www-data:www-data /usr/share/nginx/html/*
  6. Configure Nginx to work with WordPress:
    1. Open default website configuration file:
      sudo vim /etc/nginx/sites-enabled/default
    2. Change the server_name from “localhost” to your site’s IP address or DNS name:
      server_name yoursitename.com
    3. IMPORTANT: In order for WordPress Permalinks to work, do the following:
      1. In the “location /” block, change the “try_files $uri $uri/ =404;” line to:
        try_files $uri $uri/ /index.php$is_args$args;
      2. Move the entire “location / ” block to after the entire “location ~ \.php$” block.
    4. Save file and exit text editor.
    5. Restart Nginx and PHP5-FPM:
      sudo service nginx restart
      sudo service php5-fpm restart
  7. Fix logout bug.  Without this tweak, the user may get a 502 error when attempting to log out of WordPress:
    1. Open nginx.conf
      sudo vim /etc/nginx/nginx.conf
    2. Add the following lines to the “http” block:
      proxy_buffer_size 128k;
      proxy_buffers 4 256k;
      proxy_busy_buffers_size 256k;
      
      fastcgi_buffer_size  16k;
      fastcgi_buffers      16  16k;
      
    3. Save file and exit text editor.
    4. Restart Nginx again:
      sudo service nginx restart
  8. From a browser, go to your site (http://<yoursitename.com or IP Address>).  The WordPress installation screen should appear.  Complete it and submit it.  Congratulations, your site is up!  Go to section IV.

B.  MIGRATE EXISTING SITE

If you’ve done a fresh install of WordPress (III A above), skip section B and go to section IV.

Note that the following commands are to be performed on the old server that you are migrating from.  This section assumes that the old site is also hosted on a Linux machine, with mySQL (or MariaDB).  Adjust accordingly if the old server is a Windows server, or if the old server’s database is something other than mySQL or MariaDB.

  1. Export old mySQL (or MariaDB) database to text file.  Assuming that the old server has a database called wordpressdb, and a user wordpressuser that has access to it, the command would look something like this:
    mysqldump -u wordpressuser -p wordpressdb > wordpressdb.sql

    The above command will prompt you for wordpressuser‘s password, and dump the schema and data of wordpressdb into the wordpressdb.sql file.

  2. Archive (“zip”) database dump.
    tar -zcvf wordpressdb.tar.gz wordpressdb.sql
  3. Archive old site’s PHP files.  Assuming the old WordPress files are in the /var/www/htdocs directory, the command would look something like:
    tar -zcvf oldsitefiles.tar.gz /var/www/htdocs
  4. If the old site has SSL, locate the old site’s SSL certificate (.crt) and key (.key) files.  You can probably find them in the web server’s configuration files.
  5. Copy the zipped db dump (wordpressdb.tar.gz), zipped WordPress site (oldsitefiles.tar.gz), and the .crt and .key files to the new site.  I’ll leave the dear reader to figure out how to do that, whether with SCP or FTP or some other file transfer protocol if that’s what’s available.

The following commands are to be performed on the new server that you are migrating to.  This assumes that you’ve gotten the files above over from the old server to the new server, and that the files are sitting in your account’s root directory (~/).

  1. Create the WordPress database and user:
    1. Log into MariaDB as root:
      mysql -u root -p
    2. Create the WordPress database (called “wordpressdb” here, but call it whatever you want):
      CREATE DATABASE wordpressdb;
    3. Create the user for WordPress (again, call the user whatever you like):
      CREATE USER wordpressuser@localhost;
    4. Set password for this user:
      SET PASSWORD for wordpressuser@localhost = PASSWORD("yourpassword");
    5. Give wordpressuser full access to wordpressdb:
      GRANT ALL PRIVILEGES ON wordpressdb.* to wordpressuser@localhost 
      IDENTIFIED BY 'yourpassword';
    6. Refresh privileges:
      FLUSH PRIVILEGES;
    7. Exit the MariaDB shell:
      exit
  2. Import the database dump:
    1. Unpack the database dump:
      tar -zxvf wordpressdb.tar.gz
    2. Open the dump in a text editor:
      vim wordpressdb.sql
    3. If at the top of the file, there are any CREATE DATABASE or USE statements, delete those lines.
    4. Save the file and exit the text editor.
    5. Import the dump file:
      mysql -u root -p wordpressdb < wordpressdb.sql
  3. If your site’s URL remains the same as your old site, skip this step.  If your URL is changing, you will need to change the home and siteurl settings in the database:
    1. Log into MariaDB:
      mysql -u root -p
    2. Switch to your database:
      USE wordpressdb;
    3. Change the home and siteurl options:
      UPDATE wp_options SET option_value = 'http://your-new-url.com' WHERE option_name = 'home' OR option_name = 'siteurl';
    4. Exit the database
      exit;
  4. Unpack wordpress files:
    tar -zxvf oldsitefiles.tar.gz
  5. Configure WordPress to work with the imported database.
    1. Open wp-config.php in your favorite editor (like vim).  Assuming that the original files were zipped from /var/www/htdocs:
      vim htdocs/wp-config.php
    2. Find the DB_NAME, DB_USER, DB_PASSWORD, and DB_HOST definitions and change them to the database settings that you set above:
      // ** MySQL settings - You can get this info from your web host ** //
      /** The name of the database for WordPress */
      define('DB_NAME', 'wordpressdb');
      
      /** MySQL database username */
      define('DB_USER', 'wordpressuser');
      
      /** MySQL database password */
      define('DB_PASSWORD', 'yourpassword');
      
      /** MySQL hostname */
      define('DB_HOST', 'localhost');
      
    3. Save the file and exit the text editor.
  6. Copy the WordPress PHP files to the root directory of your website (the following assumes Nginx root directory from later versions of Ubuntu; check your version):
    sudo cp -rv htdocs/* /usr/share/nginx/html/
  7. Adjust permission ownership on the WordPress files so that Nginx can access them:
    sudo chown -R www-data:www-data /usr/share/nginx/html/*
  8. Configure Nginx to work with WordPress:
    1. Open default website configuration file:
      sudo vim /etc/nginx/sites-enabled/default
    2. Change the server_name from “localhost” to your site’s IP address or DNS name:
      server_name yoursitename.com
    3. IMPORTANT: In order for WordPress Permalinks to work, do the following:
      1. In the “location /” block, change the “try_files $uri $uri/ =404;” line to:
        try_files $uri $uri/ /index.php$is_args$args;
      2. Move the entire “location / ” block to after the entire “location ~ \.php$” block.
    4. Save file and exit text editor.
    5. Restart Nginx and PHP5-FPM:
      sudo service nginx restart
      sudo service php5-fpm restart
  9. Fix logout bug.  Without this tweak, the user may get a 502 error when attempting to log out of WordPress:
    1. Open nginx.conf
      sudo vim /etc/nginx/nginx.conf
    2. Add the following lines to the “http” block:
      proxy_buffer_size 128k;
      proxy_buffers 4 256k;
      proxy_busy_buffers_size 256k;
      
      fastcgi_buffer_size  16k;
      fastcgi_buffers      16  16k;
      
    3. Save file and exit text editor.
    4. Restart Nginx again:
      sudo service nginx restart
  10. From a browser, go to your site (http://<yoursitename.com or IP Address>).   Congratulations!  Your old site should be up and running on your new server.

IV. CONFIGURE SSL

As a reminder, our goal here is to force SSL on login pages and wp-admin pages.  Fortunately, Pothi Kalimuthu was generous enough to post his configuration files to Github, which cover the same issues almost completely.  I’ve borrowed heavily from these, especially admin-over-ssl.conf.

Also, this section assumes that you’ve either migrated your SSL certificate from your old site, or you’ve already purchased, requested, and downloaded a new certificate from a trusted certificate authority (CA).  Your CA probably has good instructions on its web site for obtaining an SSL certificate.

Assuming that your certificate files are in ~/.ssl, here are the directions, step by step:

  1. Place your site’s certificate file (.crt) in a good place on your server.  I recommend /etc/ssl/certs.
    sudo cp ~/.ssl/yoursitename.com.crt /etc/ssl/certs
  2. Place your site’s private key file (.key) in a safe place on your server.  I recommend /etc/ssl/private.
    sudo cp ~/.ssl/yoursitename.com.key /etc/ssl/private
  3. Open up the default configuration for nginx one more time:
    sudo vim /etc/nginx/sites-enabled/default
  4. At the beginning of the “location ~ \.php$” block (after the open bracket, inside the block), insert the following code:
    location ~ /wp-(admin|login) {
        return 301 https://$host$request_uri;
    }
    

    This code above redirects any requests for URLs that contain “wp-admin” or “wp-login” to HTTPS (i.e. SSL).

  5. Paste the following code for the SSL virtual host for in its own primary top-level block (i.e. NOT in the same “server” block as “listen 80”):
    server {
        listen 443;
        server_name yoursitename.com; #CHANGE THIS TO YOUR SITE
        index index.php;
    
        # Replace the path with the actual path to WordPress core files
        root /usr/share/nginx/html;
    
        # Generate Certificates
        # http://wiki.nginx.org/HttpSslModule#Generate_Certificates
        ssl on;
        ssl_certificate /etc/ssl/certs/yoursitename.com.crt; #CHANGE THIS FROM yoursitename.com TO YOUR SITE'S NAME
        ssl_certificate_key /etc/ssl/private/yoursitename.com.key; #THIS TOO
    
        # Logs
        access_log /var/log/nginx/yoursitename.com-access.log; #CHANGE THIS FROM yoursitename.com TO YOUR SITE'S NAME
        error_log /var/log/nginx/yoursitename.com-error.log; #THIS TOO
    
        # Processes requests to wp-admin/* and wp-login.php
        location ~ /wp-(admin|login) {
            location ~ \.php$ {
                try_files                $uri =404;
                include "fastcgi_params";
                fastcgi_split_path_info  ^(.+\.php)(/.+)$;
    
                fastcgi_index            index.php;
                fastcgi_param            SCRIPT_FILENAME    $document_root$fastcgi_script_name;
                fastcgi_intercept_errors on;
                fastcgi_pass              unix:/var/run/php5-fpm.sock;
            }
        }
    
        # Redirects all other non-admin and non-login traffic to HTTP
        location / {
            return 301 http://$host$request_uri;
        }
    
    }
    
  6. Make sure that you remember to change the instances of “yoursitename.com” to your actual site’s domain name or IP address (see the helpful comments in the virtual host code above).
  7. Save the file and exit the text editor.
  8. Restart Nginx and PHP:
    sudo service nginx restart
    sudo service php5-fpm restart
  9. Navigate to http://<yoursitename.com>/wp-admin.  Make sure that you’re directed to https://<yoursitename.com>/wp-admin, and that the login page is also https.
  10. Congratulations!  SSL has been properly configured.

Again, please let me know in the comments if there are any inaccuracies, inefficiencies, or if anything is just plain unclear in the above.  I hope that this guide saves some people some time.

 

Tagged with:
 

8 Responses to Installing LEMP (Linux, Nginx, MariaDB, PHP) for WordPress, with selective SSL.

  1. Avadhoot says:

    @Aviv,

    Great post.

    You will surely like EasyEngine. It’s really easy to setup and works at a fastening speed.

    All it takes is 3 commands and merely half an hour to setup the server:

    curl -sL rt.cx/ee | sudo bash # install easy-engine

    ee system install # install nginx, php, mysql, postfix

    ee site create example.com –wp # install wordpress on example.com

    That’s it 🙂

    Check out more: https://rtcamp.com/easyengine

    Thanks,
    -Avadhoot from @easyengine

  2. […] Installing LEMP (Linux, Nginx, MariaDB, PHP) for WordPress, with selective SSL. […]

  3. MakLaN says:

    Hi,

    I’ve setup and configured my LEMP on EC2 Instance by following this tutorial (although I’m using RHEL as instance it should be OK and I’m able to setup the webserver successfully eg. all services active). The only thing is I can’t access the server via web browser neither IP address nor the server public DNS. Any step I missed? Or should change permission for some directories of files?

    Thanks.

Leave a Reply

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

Set your Twitter account name in your settings to use the TwitterBar Section.