Install WordPress on Debian Jessie LAMP stack with Nginx Reverse Proxy

By | June 2, 2015

In this post we will see How to Install WordPress on Debian Jessie LAMP stack with Nginx Reverse Proxy.

This will be a fairly long post as we will see how get a working LAMP stack up and running first and the Install nginx, enable reverse proxy and do some performance optimization in it, like enabling gzip compression, static file caching, and passing dynamic contents to apache.

1) Setting up Postfix SMTP listening to localhost and Install Logwatch (Optional).

Postfix is not necessary as Debian comes with EXIM4 which takes care of SMTP. I prefer postfix over it. But you may definitely like to see the logwatch installation. You may click this link to see how to do it.

2) Install MySql or MariaDB database server

The repositories of Debian Jessie come with both MySql 5.5 and MariaDB 10.0 database servers. Install anyone of your choice. I am installing MySql, if you install MariaDB then just replace mysql-server with mariadb-server

apt-get install mysql-server

During installation you will be asked to set database root password, make sure that you use a strong password. Once installed, we need to secure our database server with following command.


The output listed below should be good enough for a newbie to understand how to secure mysql


In order to log into MySQL to secure it, we’ll need the current
password for the root user. If you’ve just installed MySQL, and
you haven’t set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
OK, successfully used password, moving on…

Setting the root password ensures that nobody can log into the MySQL
root user without the proper authorisation.

You already have a root password set, so you can safely answer ‘n’.

Change the root password? [Y/n] n
… skipping.

By default, a MySQL installation has an anonymous user, allowing anyone
to log into MySQL without having to have a user account created for
them. This is intended only for testing, and to make the installation
go a bit smoother. You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] y
… Success!

Normally, root should only be allowed to connect from ‘localhost’. This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] y
… Success!

By default, MySQL comes with a database named ‘test’ that anyone can
access. This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] y
– Dropping test database…
ERROR 1008 (HY000) at line 1: Can’t drop database ‘test’; database doesn’t exist
… Failed! Not critical, keep moving…
– Removing privileges on test database…
… Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] y
… Success!

Cleaning up…

All done! If you’ve completed all of the above steps, your MySQL
installation should now be secure.

Thanks for using MySQL!

3) Create the database to be used for wordpress

mysql -u root -p
create database wordpress;
grant all on wordpress.* to 'wpuser'@'localhost' identified by 'abc123';
flush privileges

I am using a simple password abc123 in this tutorial. Choose a strong password for yourself. So we have a database called wordpress and the databse user wpuser has complete privileges on it. We will use it during wordpress installation.

4) Install Apache WebServer with event mpm and PHP5-FPM

Edit ‘/etc/apt/sources.list‘ file and Enable the contrib and non-free repositories. you just need to add the values in red to the jessie main repository lines.

deb jessie main contrib non-free
deb-src jessie main contrib non-free

Update the repositories metadata and install the packages

apt-get update
apt-get install libapache2-mod-fastcgi php5-fpm apache2-mpm-event php5-gd php5-mysql php5-curl php5-pspell

Then create the file ‘/etc/apache2/conf-available/php5-fpm.conf' with the contents:

<IfModule mod_fastcgi.c>
AddHandler php5-fcgi .php
Action php5-fcgi /php5-fcgi
Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php5-fpm.sock -pass-header Authorization
<Directory /usr/lib/cgi-bin>
    Require all granted

Enable the new modules

a2enmod actions fastcgi alias
a2dismod mpm_prefork php5
a2enconf php5-fpm
a2enmod mpm_event
a2enmod rewrite
service apache2 restart

Becuase our Apache Server will sit behind a proxy, In this step we will install an Apache module named mod_rpaf which rewrites the values of REMOTE_ADDR, HTTPS and HTTP_PORT based on the values provided by a reverse proxy. Without this module, some PHP applications would require code changes to work seamlessly from behind a proxy.

apt-get install libapache2-mod-rpaf

Now its time to make a few changes with how php5-fpm works and connect it to nginx. Open the main php-fpm configuration file

sudo nano /etc/php5/fpm/php.ini

Find the parameter that sets “cgi.fix_pathinfo”. This will be commented out with a semi-colon (;) and set to “1” by default.

This is an extremely insecure setting because it tells PHP to attempt to execute the closest file it can find if a PHP file does not match exactly. This basically would allow users to craft PHP requests in a way that would allow them to execute scripts that they shouldn’t be allowed to execute.

We will change both of these conditions by uncommenting the line and setting it to “0” like this:


Now we will test the working of Apache Webserver and php

echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
/etc/init.d/apache2 restart

Now just type the url http://your-server-ip/info.php, the image below shows the output of working LAMP stack.


So our LAMP stack is functional.

5) Download wordpress and keep the setup ready

cd /tmp && wget && tar -xf latest.tar.gz
touch wordpress/.htaccess 
cp -R wordpress /var/www/
chown -R www-data.root /var/www/wordpress

Navigate inside the directory “/var/www/wordpress”. copy the file “wp-config-sample.php” as “wp-config.php”

cp -R wp-config-sample.php wp-config.php

Once copied, update the file with your wordpress database details as created above. The changes will be made in the following sections in the file:

/** The name of the database for WordPress */
define('DB_NAME', 'wordpress');
/** MySQL database username */
define('DB_USER', 'wpuser');
/** MySQL database password */
define('DB_PASSWORD', 'abc123');
/** MySQL hostname */
define('DB_HOST', 'localhost');

6) Prepare the Apache backend

We will now disable the default site and make Apache listen to an alternate port 8080. So that Nginx listens to all the incoming http connections on port 80, and servers the clients by proxying those request to apache backend.

a2dissite 000-default
touch /etc/apache2/sites-available/wordpress.conf

put contents in it as follows using nano editor

nano /etc/apache2/sites-available/wordpress.conf

<VirtualHost *:8080>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/wordpress
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory /var/www/wordpress>
AllowOverride All

In this virtual host definition, we are making apache to listen to port 8080. We also have to make Apache to listen to port 8080 in ‘/etc/apache2/ports.conf‘ file as well, just find the line ‘Listen 80′ in it and change it to ‘Listen 8080′, and restart apache.

a2ensite wordpress
/etc/init.d/apache2 restart

7) Install Nginx and enable reverse proxy feature

apt-get install nginx

The main nginx configuration file resides in ‘/etc/nginx‘ , and it is known as ‘nginx.conf‘. The contents of file that I used is pasted below for your reference. I have enabled gzip and some other basic optimization features:

user www-data;
worker_processes 1;
pid /run/;
events {
worker_connections 1024;
multi_accept on;
use epoll;
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 20;
client_max_body_size 15m;
client_body_timeout 60;
client_header_timeout 60;
client_body_buffer_size 1K;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
send_timeout 60;
reset_timedout_connection on;
types_hash_max_size 2048;
server_names_hash_bucket_size 64;
server_tokens off;
server_name_in_redirect off;                                                                                      open_file_cache max=2000 inactive=20s;
open_file_cache_valid 60s;
open_file_cache_min_uses 5;
open_file_cache_errors off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_static on;
gzip_disable “msie6”;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 512;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/ image/svg+xml image/x-icon;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

Next, we will remove the default virtual host config and create a new one with our proxy definitions as well as the static content caching parameters.

rm /etc/nginx/sites-enabled/default
touch /etc/nginx/sites-available/example
vi /etc/nginx/sites-available/example

The contents of the new virtual host called example is listed below:

server {
root /var/www/wordpress/;
access_log /var/log/nginx/example.com_access.log;                                                                         error_log /var/log/nginx/example.com_error.log;
#####Caching Static contents######
location ~* ^.+.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js)$ {
expires 31536000s;
add_header Pragma public;
add_header Cache-Control “max-age=31536000”;
location ~* “^.*?\.(eot)|(ttf)|(woff)|(woff2)$” {
#add_header Access-Control-Allow-Origin *;
expires 31536000s;
add_header Pragma public;
add_header Cache-Control “max-age=31536000”;
######Forwarding Dynamic Content to Apache######
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;

##### Stuff In our Web Root That Should not be accessible to the outside world ######
location ~ /\. {
deny all;
location ~ /(\.|wp-config.php|readme.html|license.txt) {
return 404;
#deny all;

Put your own server name instead of Next, we will enable the the virtual host ‘example’ and restart nginx.

cd /etc/nginx/sites-enabled
ln -s ../sites-available/example

9) Lets start the wordpress installation

Just open your web browser and type http://your_domian_name, and installer will start like the images below:





So this is How to Install WordPress on Debian Jessie LAMP stack with Nginx Reverse Proxy.



6 thoughts on “Install WordPress on Debian Jessie LAMP stack with Nginx Reverse Proxy

  1. Phil

    Thank you, great post!

    There is a typo where you create the soft link in nginx sites-enabled, should be:
    ln -s ../sites-available/example

    1. rudraraj Post author

      Thanks for your word of appreciation and pointing out the error. I have updated the post.

  2. Jerry

    Hi rudraraj,
    I’m following above tutorial, but end up with ” Index of / ” page instead of wordpress installation page when opening in web browser.
    Any idea what may cause it?

    1. rudraraj Post author

      With respect to this tutorial, What I have done is I have downloaded wordpress tarball and unpacked it and copied it in /var/www/wordpress location. And this location is acting as my web root, which means that I have defined the same as my web root at apache level virtual host definition and that of nginx as well. Now coming to your question, you may see the “index of /” page if you have unpacked wordpress in one directory but not defined it as your webroot. I will suggest you to read this tutorial a couple of times and then replicate it at your end. It should work.

      1. Jerry

        Thanks for the reply,
        Now I’ve read it couple of times, there’s one important thing miss in your tutorial:
        “In apache backend, the wordpress site is not being enabled (a2ensite wordpress)”
        Appreciate your tutorial, it really help me.

        1. rudraraj Post author

          Thanks for pointing it out, I have corrected it (Advantages of reading you see..!!! 😉 ). It seems the a2ensite command got accidentally deleted while I was trying to correct a few typos in this tutorial last week. I hope your wordpress site is working as desired for you now.


Leave a Reply

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