KBD

Keith Devens .com

Friday, September 5, 2008 Flag waving
"Child," said the Voice, "I am telling you your story, not hers. I tell no one any story but his... – C.S. Lewis (The Horse and His Boy, ch 11)

Tag: Mod_rewrite

Daily link icon Monday, May 24, 2004

Clean URLs with mod_rewrite and PHP

What follows are the two main pieces of mod_rewrite that I ever use. I've posted these in separate places before, so this is mostly just to aggregate them in one place, as well as to explain how I make sure all URLs on my site are "clean".

First, these rules pass any request for a file that doesn't exist through the "dispatcher", a PHP script which can then do whatever it wants to. It serves as a Front controller (in that article's terminology, Apache acts as the "controller" while dispatcher.php acts as the "dispatcher"). Importantly, note that with the second line commented out, like I have it, this also bypasses directory handling (mod_dir) which means that any index.php, index.html, etc. will never be called. I prefer it this way mainly because I don't like mod_dir sticking trailing slashes on the end of any my URLs. I have a strict policy that no URL on my site can end with a slash (try appending a slash to a URL on my site, I dare you). The way I do it, no URLs have extensions (except images and things that I want to be served directly by Apache) so everything can look like a directory anyway.

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
#RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /dispatcher.php [L]

All of the mod_rewrite directives I list here I have in my .htaccess file in my document root. Note that the slash before dispatcher.php is important, as an analysis of mod_rewrite logs shows that while it would work without the leading slash, having the leading slash makes the translation take fewer steps (which is presumably more efficient).

The way I handle my content is that I have a special "content" directory outside of my document root and my dispatcher parses the URL and serves up what you want if it exists. This way I avoid cluttering up my document root. I don't personally use the following rewrite rules because of this, and because I serve things through the front controller rather than directly, but if you use the following rules, any .php file in your document root will be able to be served without an extension.

RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule . %{REQUEST_FILENAME}.php [L]

Of course, this has to go before the above directives, otherwise it won't be run because dispatcher will get things first. To prevent the files from being accessed with the .php extension, you may want to implement some logic, possibly using auto_prepend_file, to do a permanent redirect from the .php version to the sans-.php version. Because the regular expressions in mod_rewrite don't support non-greedy matching, I don't think there's any way do that redirect in mod_rewrite itself, you need to use PHP for that. It can be as simple as something like:

<?php
if(substr($filename, -4) == '.php')
    
setHttpLocation(substr($filename,0,-4)); #take off the .php
?>

In fact, that's taken almost directly from my dispatcher. setHttpLocation() is straightforward -- leaving it in there makes it work as good pseudocode.

Then, to handle subdirectories in this scheme, you can use subdir.php to represent the subdirectory root (instead of having something like subdir/index.php), and then subdir/whatever.php to handle subdir/whatever).

The other way you can make clean URLs with PHP is by using the following directive in your .htaccess file:

DefaultType application/x-httpd-php

That will make any file not recognized by Apache, including files without extensions, be served as PHP, but this creates problems if you want to have subdirectories since you can't have a file and a directory with the same name.

Update (Jul 9, 04): Note that if you use the dispatcher.php mod_rewrite rules above, you should also add dispatcher.php to your DirectoryIndex setting.

Update (Jul 13, 04): Also see this. You'll probably want to change one of the above lines to "RewriteRule . %{REQUEST_FILENAME}.php%{PATH_INFO} [L]".

Daily link icon Sunday, May 23, 2004

Does mod_rewrite's last flag do anything?

Does mod_rewrite's last flag do anything? These rules wind up giving a forbidden error:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . dispatcher.php [L]
RewriteRule . - [f]

That says: "If the requested filename doesn't exist, forward any URL to dispatcher.php and stop processing rules". Yet instead of stopping at the dispatcher line, it continues onto the next and makes the page forbidden. Why?

Daily link icon Wednesday, November 12, 2003

Send all requests to a single script with mod_rewrite

This is what I have in my Apache .htaccess file that sends all requests to my CMS. It checks first if the request is for an actual file or directory, and if it's not it forwards everything to "dispatcher.php"

RewriteEngine On

#if the request isn't for a file or a directory...
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . dispatcher.php

Daily link icon Saturday, August 17, 2002

Fun with mod_rewrite

I've been learning mod_rewrite today. I'm thinking of writing a CMS, and I need to be able to translate "fake" URLs (requests for files that don't actually exist) into objects in my database. Importantly, however, I want to make sure that if an actual file exists, that file gets served instead of looking in my CMS. So, here are my rules:

RewriteEngine On
RewriteBase /testing/rewritetest/
RewriteRule ^([^.]+[^/])$ $1/ [R=permanent,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ bar.php?$1

Basically, this says "start the rewriting engine" (start processing rules) and set the base directory. If the thing you requested looks like a directory (if it doesn't have an extension), then add a trailing slash and make it look like a directory. This sends an actual redirect (code 301, "moved permanently") to the browser, and stops processing other rules (L means "last").

Otherwise, if it's either a "real looking" directory with a slash on the end, or it looks like a file (has an extension), then if what you requested isn't a real directory and isn't a real file, fulfill the request with bar.php and pass it the actual requested URL through the querystring.

This is, of course, just a test, and filenames, etc. will be changed for my actual CMS.

There was a bug in Apache for Windows that caused this to break when I put it on my system. I found this in the release announcement for Apache 1.3.26, which happens to be the version I'm using:

Fix for a problem in mod_rewrite which would lead to 400 Bad Request responses for rewriting rules which resulted in a local path.

Even though they said the bug was fixed, this seemed to be the same behavior I was getting. In fact, the engine was erroneously injecting the local path into the filename twice when it resolved the URL into a filename. As you can see in the log I generated (some info deleted):

(3) [per-dir c:/prog/work/localweb/testing/rewritetest/] add per-dir prefix: bar.php -> c:/prog/work/localweb/testing/rewritetest/bar.php
(3) [per-dir c:/prog/work/localweb/testing/rewritetest/] add per-dir prefix: c:/prog/work/localweb/testing/rewritetest/bar.php -> c:/prog/work/localweb/testing/rewritetest/c:/prog/work/localweb/testing/rewritetest/bar.php

So, that's definitely wrong. But I noticed they said the bug was for when you resolved to a local path, so I cheated and changed it to an absolute path:

RewriteRule ^(.+)$ http://localhost/testing/rewritetest/bar.php?$1

And that fixed it.

September 2008
SunMonTueWedThuFriSat
 123456
78910111213
14151617181920
21222324252627
282930 



RSS feed RSS feed for Keith's Weblog
Atom feed Atom feed for Keith's Weblog
Weblog archive
Recent comments
  on 5 posts

Recent comments XML

new⇒I hate ASP.NET

CF, why pick that piece of trash?​Cold Confusion. Is it finally​really a OO...

ColdConfusion: Sep 5, 8:36pm

new⇒Maps of Iraq

This is for Linda, I will be​visiting that site some time in the​near futur...

Bob: Sep 5, 1:20pm

Girls, please don't get breast implants

Well alright I just read my above​comment and I wanted to add​this...I shou...

76.66.140.8: Sep 4, 7:31pm

Spider solitaire

I don't think the question was​necessarily if there are unbeatable​games.  ...

Jared: Sep 4, 12:44pm

Convert Pantone Colors to RGB and Hex - Color Conversion Chart

The colors on those website don't​seem to relate to the pantone data​we hav...

blah: Sep 3, 10:12am

Generated in about 0.217s.

(Used 10 db queries)

mobile phone