For years, I’ve relied on Apache web server on my personal machine to continually hone my web programming chops and it doesn’t hurt to readily have a working server configuration for quick reference.
While Apache has performed brilliantly, it was time to switch and get more comfortable with NGINX. It is frequently chosen to reverse proxy and route HTTP traffic towards remote processes which actually ingest the web requests and process them.
In the real world, with reverse proxying, NGINX can manage many thousands of requests by offloading the dirty work to other servers to handle. In my small development setup with all the required processes running on the same machine, NGINX merely hands off the work onto these local services and it all stays on the same machine.
I like to have several resources at my disposal to muck around with. These include:
- WordPress blog site which is hosted at the default location ‘/’
- phpMyAdmin which greatly simplifies MySQL database administration stuff.
- Playground for my PHP programming stuff
- Playground for my Python progamming stuff
- A Django web framework site that I can beat up on
- Playground for my Go programming stuff.
- Playground for Flask web framework
My desired configuration is pictured below:

The below lists all the steps to get a fully working NGINX reverse proxy to all these resources, located at their respective branch off the default web domain.
Most tutorials out there will tell you how to set up each of these resources but assumes that you want it to be seen at the default ‘/’ location which doesn’t work well when you want multiple stuff running at the same time. It makes more sense to split them off into their own branch off the default web location.
This was done on a vanilla Ubuntu system. I used the localhost server since this was limited to my machine for development reasons. Adjust as needed for a publicly accessible server with a fully qualified domain name.
Reverse proxy to phpmyadmin and default WordPress site
The goal here:
http://site.com/ → to wordpress
http://site.com/phpmyadmin → to phpmyadmin
sudo apt-get install nginx
sudo service nginx start
sudo apt-get install phpmyadmin (ignore when asked for apache2 or lighttpd)
phpmyadmin installed in /usr/share/phpmyadmin
sudo apt-get install php5-fpm
sudo vi /etc/php5/fpm/php.ini and edit:
cgi.fix_pathinfo=0
sudo vi /etc/php5/fpm/pool.d/www.conf and edit:
listen = /var/run/php5-fpm.sock
sudo service php5-fpm restart
sudo vi /etc/nginx/sites-available/default
(This is not the final configuration but will get phpmyadmin working)
note that: /phpMyAdmin will be redirected to /phpmyadmin
server {
listen 80;
root /usr/share/nginx/html;
index index.php index.html index.htm;
server_name localhost;
location / {
try_files $uri $uri/ /index.html;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# pass the PHP scripts to FastCGI server listening on the php-fpm socket
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location /phpmyadmin {
root /usr/share/;
index index.php index.html index.htm;
location ~ ^/phpmyadmin/(.+\.php)$ {
try_files $uri =404;
root /usr/share/;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* ^/phpmyadmin/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ {
root /usr/share/;
}
}
location /phpMyAdmin {
rewrite ^/* /phpmyadmin last;
}
}
service nginx reload
Reverse proxy to Default WordPress site
WordPress was already installed in /var/www/www.jaredlog.com so the steps for installation of WordPress won’t be covered here. Plenty of online tutorials out there to help you with this.
edit /etc/nginx/sites-available/default
root /var/www/www.jaredlog.com;
index index.php index.html index.htm;
server_name localhost;
location / {
try_files $uri $uri/ /index.html;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# pass the PHP scripts to FastCGI server listening on the php-fpm socket
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
Reverse proxy to uWSGI python with unix socket
The goal here:
http://site.com/pythontest → to Python playground
With python stuff, we will make use of virtual environments to simplify the versioning of python and included packages.
sudo apt-get install python-dev python-pip
sudo pip install virtualenv
cd /var/www/pythontest
virtualenv pytontestenv
Activate the virtual environment
source pythontestenv/bin/activate
(deactivate later on after installation of packages).
pip install uwsgi
uwsgi –version
Setup a simple uWSGI app with unix socket for less overhead
(the Django w/Guincorn setup is mentioned later on)
edit /var/www/pythontest/wsgi.py
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return ["<h1 style='color:blue'>Hello There, this is WSGI!</h1>"]
Test it
uwsgi –socket 0.0.0.0:8080 –protocol=http -w wsgi
Open web browser and visit http://localhost:8080
Can deactivate virtual environment now
deactivate
edit /var/www/pythontest/pythontest.ini
[uwsgi]
module = wsgi:application
master = true
processes = 5
socket = pythontest.sock
chmod-socket = 664
vacuum = true
die-on-term = true
edit /etc/init/pythontest.conf
description "uWSGI instance to serve pythontest"
start on runlevel [2345]
stop on runlevel [!2345]
setuid www-data
setgid www-data
script
cd /var/www/pythontest
. pythontestenv/bin/activate
uwsgi -ini pythontest.ini
end script
sudo stop pythontest ; sleep 2 ; sudo start pythontest
See it running:
ps wwwauxg | grep uwsgi
add to /etc/nginx/sites-available/default
location /pythontest {
include uwsgi_params;
uwsgi_pass unix:/var/www/pythontest/pythontest.sock;
}
sudo service nginx configtest
sudo stop pythontest ; sleep 2 ; sudo start pythontest
sudo service nginx restart
Django installation with Gunicorn, MySQL and unix socket
The goal here:
http://site.com/djangotest → to Django playground
Create MySQL database djangotest
sudo apt-get install libmysqlclient-dev
cd /var/www/djangotest
source djangotestenv/bin/activate
pip install MySQL-python
pip install django
pip install gunicorn
deactivate
django-admin.py startproject djangotest .
edit /var/www/pythontest/djangotest/djangotest/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'OPTIONS': {
'read_default_file': '/var/www/pythontest/djangotest/my.cnf',
},
}
}
add to end of settings.py
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
Create /var/www/pythontest/djangotest/my.cnf
[client]
database = djangotest
host = localhost
user = dbuser
password = yayouwish
default-character-set = utf8
./manage.py makemigrations
./manage.py migrate
./manage.py createsuperuser
./manage.py collectstatic
edit urls.py:
urlpatterns = [
url(r’^djangotest/admin/’, include(admin.site.urls)),
url(r’^djangotest/’, include(admin.site.urls)),
]
./manage.py runserver 0.0.0.0:8000
Open web browser to localhost:8000 to confirm.
localhost:8000/admin (log into django)
Gunicorn testing
source djangotestenv/bin/activate
gunicorn –bind 0.0.0.0:8000 djangotest.wsgi:application
confirm via web browser on port 8000
deactivate
/etc/init/djangotest.conf
description "Gunicorn instance to serve djangotest"
start on runlevel [2345]
stop on runlevel [!2345]
setuid www-data
setgid www-data
script
cd /var/www/djangotest
. djangotestenv/bin/activate
/var/www/djangotest/djangotestenv/bin/gunicorn –workers 3 –bind unix:/var/www/djangotest/djangotest.sock djangotest.wsgi:application
end script
sudo stop djangotest ; sleep 2 ; sudo start djangotest
nginx configuration for django:
location /static/ {
root /var/www/djangotest;
}
location /djangotest {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/var/www/djangotest/djangotest.sock;
}
Reverse proxy to Go
The goal here:
http://site.com/gotest → to Golang playground
GVM makes it easier to develop with different versions of Go and packages your projects need. Visit: https://github.com/moovweb/gvm for more information
sudo apt-get install curl git mercurial make binutils bison gcc
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
gvm listall
gvm install go1.4.2
gvm use go1.4.2 –default
gvm list
source ~/.gvm/scripts/gvm
cd ~
mkdir -p go/{bin,pkg,src}
Put in .bashrc or .bash_profile
export GOPATH=$HOME/go
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOPATH/bin
cd ~/go/src
mkdir hello
cd hello
vi hello.go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello there, Gopher")
})
http.ListenAndServe(":"3000″, nil)
}
go run hello.go
Open web browser to localhost:3000 and confirm you see the output.
sudo vi /etc/nginx/sites-enabled/default and add:
location /gotest {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:3000;
}
Open web browser to localhost/gotest and you should see the same output
go install hello.go (installs hello binary in your ~/go/bin directory)
cp ~/go/bin/hello /var/www/gotest
edit /etc/init/gotest.conf
description "instance to serve gotest"
start on runlevel [2345]
stop on runlevel [!2345]
setuid www-data
setgid www-data
script
exec /var/www/gotest/hello
end script
stop gotest ; sleep 2 ; start gotest
Reverse proxy to Flask with unix socket
The goal here:
http://site.com/flasktest → to Flask playground
mkdir /var/www/flasktest
cd /var/www/flasktest/
virtualenv flasktestenv
. flasktestenv/bin/activate
pip install Flask
pip install uwsgi
deactivate
vi flasktest.py
from flask import Flask
app = Flask(__name__)
app.debug = True
@app.route('/flasktest')
def index():
return 'Flask Index Page'
@app.route('/flasktest/hello')
def hello():
return 'Hello World from Flask'
edit /var/www/flasktest/wsgi.py
from flasktest import app
if __name__ == "__main__":
app.run()
. flasktestenv/bin/activate
uwsgi –socket 0.0.0.0:8000 –protocol=http -w wsgi
verify working by checking via web browser at http://localhost:8000/flasktest
deactivate
/var/www/flasktest/flasktest.ini
[uwsgi]
module = wsgi
master = true
processes = 5
socket = flasktest.sock
chmod-socket = 660
vacuum = true
die-on-term = true
/etc/init/flasktest.conf
description "uWSGI instance to serve flasktest"
start on runlevel [2345]
stop on runlevel [!2345]
setuid www-data
setgid www-data
script
cd /var/www/flasktest
. flasktestenv/bin/activate
# uwsgi --ini flasktest.ini
uwsgi -s flasktest.sock --catch-exceptions --module flasktest --callable app
# uwsgi --catch-exceptions --ini flasktest.ini
end script
/etc/nginx/sites-available/default
location /flasktest {
include uwsgi_params;
uwsgi_pass unix:/var/www/flasktest/flasktest.sock;
}
stop flasktest ; sleep 2 ; start flasktest
service nginx restart
open web browser to http://localhost/flasktest
Finally, here’s the final NGINX configuration in all its glory:
default.nginx.config
If you have better ideas for the NGINX configuration than what is seen here, feel free to comment below!