Skip to main content

Using nginx rewrite's to remove the file extension and still work with PHP-FastCGI

I had been debating trying out [nginx][] for a while, and at the end of last week a project I work on had it's web server go down. I decided this was a good time as any to play with nginx when getting the new box up.

One of the rules we had on the old Apache setup however, was that we re-wrote URL's to remove their .php extension. We achieved this thru a simple rule using mod_rewrite. The rule was:

RewriteCond /var/www/site/%{REQUEST_URI}.php -f RewriteRule ^/(.+)$ /$1.php [L]

So wanting to re-implement this in nginx, I did a bit of reading of he [nginx wiki][], and while the examples on the rewrite module are limited, there was enough information to point out that the following would do what we wanted:

rewrite ^/([a-z]+)$ /$1.php last;

However, this rule doesn't play nicely with PHP-FastCGI.

I read a lot of articles, and most of these suggested that the best way to work around this, was to make all requests go to an intermediary PHP script, and have it handle all page requests. This seems a bit silly, and doesn't seem to solve the problem, but work around it.

So to get this to work, we had to look at how PHP-FastCGI works. The default setup for PHP-FastCGI looks something like this:

location ~ .php$ {
include /etc/nginx/fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /var/www/site$fastcgi_script_name;

System Message: WARNING/2 (<string>, line 23)

Definition list ends without a blank line; unexpected unindent.

}

So what this does, is when a page matches the .php extension, it will send it off to FastCGI for processing, and the second last line calculates the full path to the PHP script to run.

Now the problem with my rule is that $fastcgi_script_name is never going to contain the php script name, as because we are rewriting the URL, $fastcgi_script_name doesn't seem to update correctly.

Now, nginx does provide you with the set command in the config file, to set a variable, however, it appears you can't set all variables with it (probably a wise thing), but because we just use it in a string, we can just use another variable we have control over.

But we can't just do a rewrite and set the variable, we have to an if block.

After a bit of playing, this is what I came up with. It does the trick, and works as expected for us, but your mileage may vary.

location / {
set $page_to_view "/index.php"; try_files $uri $uri/ @rewrites; root /var/www/site; index index.php index.html index.htm;

System Message: WARNING/2 (<string>, line 40)

Definition list ends without a blank line; unexpected unindent.

}

location ~ .php$ {
include /etc/nginx/fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /var/www/site$page_to_view;

System Message: WARNING/2 (<string>, line 47)

Definition list ends without a blank line; unexpected unindent.

}

# rewrites location @rewrites {

System Message: ERROR/3 (<string>, line 51)

Unexpected indentation.
if ($uri ~* ^/([a-z]+)$) {
set $page_to_view "/$1.php"; rewrite ^/([a-z]+)$ /$1.php last;

System Message: WARNING/2 (<string>, line 54)

Definition list ends without a blank line; unexpected unindent.

}

System Message: WARNING/2 (<string>, line 55)

Block quote ends without a blank line; unexpected unindent.

}

This works by setting a new variable $page_to_view, and setting the default variable to "/index.php" (i.e. the page to view, if the request is for the root folder, e.g., http://example.nullis.net/).

Then when we get a page that means our rewrite condition, we set it to the page name we expect, and then this is used when setting the FastCGI script name. This means when they go to http://example.nullis.net/info, it actually gives you info.php, without having a proxy PHP page, or multiple rewrite rules.

Given I couldn't find a good example elsewhere, I figured it would be worth documenting this for other people. Let me know if you have a better way to go about this, as I'm not sure this is the best way, but it's definitely better.

If you do have suggestions, then please [contact me][].

[nginx]: http://www.nginx.org/ [nginx wiki]: http://wiki.nginx.org/ [contact me]: http://www.nullis.net/contact/

Google Video Shutting Down

Jason Kincaid, Techcrunch: > Google just sent out an email to users who have previously uploaded content to the service informing them that on April 29 2011, the site will no longer host any more videos.

While this is probably something a long time coming, it does mean there is the potential for there to be a lot of videos that only exist on Google Video, and haven't been migrated to other services (Youtube, Vimeo, etc.). A thread on [reddit][] is trying to list all the good ones that the userbase can find.

[reddit]: http://www.reddit.com/r/AskReddit/comments/gr2bv/in_two_weeks_all_videos_uploaded_to_google_video/

Bad advice on IPv6

Logan G. Harbaug of PCWorld: > A lot of coverage of IPv6 over the past few years gives the impression that you need to switch to IPv6 soon. That's not necessarily the case, though, especially for a consumer or a small business.

> However, it may be at least three to five years before the average small business needs to work on this.

I think many people will disagree with his argument. Not taking action soon enough on IPv6, is just going to cause people to be unprepared when it is required.

Consumers and Small Businesses would only replace hardware (and software for that matter), after the previous one has had a very long lifespan. (Hell, I know people who are still using Pentium 3 Desktops!). So to say to them "don't do anything now, do it when everyone else is ready", will mean that when it is ready, there will be a mad rush to get it sorted, and there will be no "smooth" transition (and probably cause more issues!).

So please, plan your next Software and Hardware purchases in mind of them needing IPv6 support. Chances are half of what you have, won't have IPv6 support yet.

In Other Australian TV Announcements

From The Daily Telegraph:

> Not one single viewer was recorded as watching it in Melbourne or Adelaide on Wednesday at 9.30pm on Arena.

The new Australian TV show "Park Street", which goes behind the scenes of some of Australia's most prestigious magazines, looks like it's a flop, and will most likely get cancelled before the end of the series is up.

Further in the article: > An excruciatingly low 876 viewers watched it in Sydney - an unheard of figure if you take out the Uzbekistan news at 5am on SBS - while 76 people in Perth tuned in to watch the women produce their magazines.

These figures are some of the lowest I've ever seen for a TV Show or Channel.

Live from Planet Axed

From the BBC: > British comic Ben Elton's new show in Australia has been axed after three episodes, it has been announced. > Live from Planet Earth, a combination of stand-up and sketch broadcast entirely live, was originally commissioned for six episodes.

> The show's debut episode attracted an average audience of 455,000 viewers and ratings have continued to drop, the newspaper reported.

Too much hype, led to not much of anything in the end.