Configuring Nginx & CodeIgniter with Rewrite Rules

I’ve recently begun working with Nginx as we’re finally moving off of Apache in our infrastructure. The first thing I noticed is documentation for various types of configurations is a bit sporadic. A lot is outdated and many don’t use best practices.

The first application I’m working on is a CodeIgniter-based PHP site which needs a few rewrite rules to function properly. I dug up this tutorial but it unfortunately uses a lot of if statements which I quickly learned are evil in Nginx.

Below is the configuration that I’ve settled upon (for now) which takes into account some best practices including:

  • uses php-fpm for the upstream server via a unix socket
  • redirects all http://www.example.com traffic to example.com
  • passes all requests under the root to the front-controller as long as they don’t exist (allows nginx to directly serve static files)
  • prevents uncontrolled requests from being passed to php
  • prevents any access to leftover .htaccess files

I’m hoping to add some of the additional rewrite functionality found in the tutorial at a later date.

upstream php {
server unix:/var/run/php5-fpm.sock;
}
server {
# enforce NO www
server_name http://www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
root /home/webapps/www.example.com/;
access_log /var/log/nginx/www.example.com.access.log main;
location / {
index index.php;
# pass requests to the front controller (http://wiki.nginx.org/Pitfalls#Front_Controller_Pattern_based_packages)
# but don't proxy everything (http://wiki.nginx.org/Pitfalls#Proxy_Everything)
try_files $uri $uri/ /index.php;
}
location ~ \.php$ {
# dont pass uncontrolled requests to php (http://wiki.nginx.org/Pitfalls#Passing_Uncontrolled_Requests_to_PHP)
try_files $uri =404;
fastcgi_pass php;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
# deny access to .htaccess files
location ~ /\.ht {
deny all;
}
}

view raw
example.com.conf
hosted with ❤ by GitHub

Symfony2 vs Django – Models

As promised in my first post, I’m going to be looking at the reasons I chose to go with Django rather than Symfony2. Aside from wanting to learn a new language, there were a few things that jumped out at me when looking into Django.

One of the biggest was the dramatic amount of code necessary to do the same thing in Symfony2. Let’s look at a typical model for a blog. Here’s what it looks like in Django (I’ve added a small utility function via a manager):

models/blog.py

from django.db import models
class BlogManager(models.Manager):
def get_tags(self):
# get just the tags
tags = self.values('tags')
# combine all tags into a single list
all_tags = []
for tag in tags:
all_tags += tag.values()[0].split(',')
# strip whitespace
stripped_tags = [tag.strip() for tag in all_tags]
return stripped_tags
class Blog(models.Model):
title = models.CharField(max_length=255)
slug = models.CharField(max_length=255)
author = models.CharField(max_length=100)
blog = models.TextField()
image = models.CharField(max_length=20)
tags = models.TextField()
created = models.DateTimeField('date created', auto_now_add=True)
updated = models.DateTimeField('last updated', auto_now_add=True, auto_now=True)
objects = BlogManager()
def __unicode__(self):
return self.title
class Meta:
app_label = 'app'

view raw
blog.py
hosted with ❤ by GitHub

Now here’s the same model in Symfony2 along with the utility function:

Entity/Blog.php

<?php
namespace NewCoInc\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use NewCoInc\AppBundle\Lib\FormattingLib;
/**
* Blog
*
* @ORM\Table(name="blog")
* @ORM\Entity(repositoryClass="NewCoInc\AppBundle\Entity\BlogRepository")
*/
class Blog
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="title", type="string", length=255, nullable=false)
*/
private $title;
/**
* @ORM\Column(type="string")
*/
private $slug;
/**
* @var string
*
* @ORM\Column(name="author", type="string", length=100, nullable=false)
*/
private $author;
/**
* @var string
*
* @ORM\Column(name="blog", type="text", nullable=false)
*/
private $blog;
/**
* @var string
*
* @ORM\Column(name="image", type="string", length=20, nullable=false)
*/
private $image;
/**
* @var string
*
* @ORM\Column(name="tags", type="text", nullable=false)
*/
private $tags;
/**
* @ORM\OneToMany(targetEntity="Comment", mappedBy="blog")
*/
private $comments;
/**
* @var \DateTime
*
* @ORM\Column(name="created", type="datetime", nullable=false)
*/
private $created;
/**
* @var \DateTime
*
* @ORM\Column(name="updated", type="datetime", nullable=false)
*/
private $updated;
public function __construct()
{
$this->comments = new ArrayCollection();
$this->setCreated(new \DateTime());
$this->setUpdated(new \DateTime());
$this->formatter = new FormattingLib();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* @param string $title
* @return Blog
*/
public function setTitle($title)
{
$this->title = $title;
$this->setSlug($this->title);
return $this;
}
/**
* Get title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set author
*
* @param string $author
* @return Blog
*/
public function setAuthor($author)
{
$this->author = $author;
return $this;
}
/**
* Get author
*
* @return string
*/
public function getAuthor()
{
return $this->author;
}
/**
* Set blog
*
* @param string $blog
* @return Blog
*/
public function setBlog($blog)
{
$this->blog = $blog;
return $this;
}
/**
* Get blog
*
* @return string
*/
public function getBlog()
{
return $this->blog;
}
/**
* Set image
*
* @param string $image
* @return Blog
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* @return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set tags
*
* @param string $tags
* @return Blog
*/
public function setTags($tags)
{
$this->tags = $tags;
return $this;
}
/**
* Get tags
*
* @return string
*/
public function getTags()
{
return $this->tags;
}
/**
* Set created
*
* @param \DateTime $created
* @return Blog
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* @param \DateTime $updated
* @return Blog
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Add comments
*
* @param \NewCoInc\AppBundle\Entity\Comment $comments
* @return Blog
*/
public function addComment(\NewCoInc\AppBundle\Entity\Comment $comments)
{
$this->comments[] = $comments;
return $this;
}
/**
* Remove comments
*
* @param \NewCoInc\AppBundle\Entity\Comment $comments
*/
public function removeComment(\NewCoInc\AppBundle\Entity\Comment $comments)
{
$this->comments->removeElement($comments);
}
/**
* Get comments
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getComments()
{
return $this->comments;
}
/**
* Set slug
*
* @param string $slug
* @return Blog
*/
public function setSlug($slug)
{
$this->slug = $this->formatter->slugify($slug);
return $this;
}
/**
* Get slug
*
* @return string
*/
public function getSlug()
{
return $this->slug;
}
}

view raw
Blog.php
hosted with ❤ by GitHub

Entity/BlogRepository.php

<?php
namespace NewCoInc\AppBundle\Entity;
use Doctrine\ORM\EntityRepository;
class BlogRepository extends EntityRepository
{
public function getTags()
{
$blogTags = $this->createQueryBuilder('b')
->select('b.tags')
->getQuery()
->getResult();
$tags = array();
foreach ($blogTags as $blogTag)
{
$tags = array_merge(explode(",", $blogTag['tags']), $tags);
}
foreach ($tags as &$tag)
{
$tag = trim($tag);
}
return $tags;
}
}

view raw
BlogRepository.php
hosted with ❤ by GitHub

Wow, that’s a TON of code. I will mention that Symfony2 does offer some tools to help generate this code, which are very helpful. But it feels to me like they’re trying to overcome the shortcomings of the language.

Time for a Switch? PHP / Symfony2 vs Python / Django

UPDATE: Check this post for a comparison of Symfony2 and Django models.

UPDATE 2: For a framework speed comparison, check out this post.


For the past few years I’ve been using the CodeIgniter PHP framework for projects at TBWA. It’s a simple, straightforward framework that has done a solid job. New devs can get up to speed quickly on it with very little training and the framework itself is speedy enough for my purposes. But things have progressed significantly over the past couple years. I’m about to embark on a new project and it’s possibly time for something new.

After a bit of research I narrowed things down to sticking with PHP and moving to the Symfony2 framework or moving over to Python / Django. I won’t get into a debate of what language is better, faster, more elegant, more awesome, etc. There’s plenty of that out there already.

For me the choice is simple, use a language I know or make the switch to something new. Final destination for this will be a 3-tier set up on AWS or a similar cloud provider. I’m using RightScale which provides ServerTemplates for both PHP and Django, making tuning and rapid deployment (via Chef) about the same for both. So the implications of running servers for one vs the other aren’t necessarily a concern (yes, performance is a valid issue but I believe either framework can be tuned to achieve acceptable throughput as demonstrated by the sites currently using them).

Before diving into Python I decided to quickly learn Symfony2 first since I already know PHP. The framework site has a ton of clearly written docs so it was easy to get up to speed. This tutorial was also very helpful. There’s a lot going on with Symfony2 – Composer for dependency management, Doctrine for ORM, Twig for templating, Assetic for asset management, PHPUnit for testing, etc. Of course all of these can be used without Symfony2, but the framework does a nice job of making them all play nicely together. It’s clearly where PHP development is headed. It you choose this direction, I highly recommend PhpStorm as your IDE as it’s got built-in support for the framework.

With that out of the way it was time to learn some Python / Django. Having zero experience with either the language or the framework, I decided to dive right into the Django docs, picking up Python along the way. Again, many of the same features can be found with this setup – pip for dependency management, Django’s built-in ORM and templating engine, webassets for asset management, unittest2 for testing, etc. Django also has a simple admin interface that can be automatically generated which I’m sure would come in handy for some projects. The getting started docs are a little sparse in comparison but a quick Google search will usually answer any question. And again, Jetbrains has a great IDE for Django development – PyCharm.

One last factor to take into account was who is behind these frameworks. On the Symfony2 side we’ve got SensioLabs (a French digital agency) and Fabien Potencier along with multiple contributors. Django, on the other hand, is backed by the non-profit Django Software Foundation with a governance board, core team and multiple contributors. License wise, Symfony2 is MIT while Django is BSD.

Based on the title you’ve probably guessed I’ve selected Django. But first, I’ll use the next couple posts to explore some of the differences in the frameworks as well as development workflow and configuration.