Page 1 of 1

help with apache rewrite rule

Posted: Tue Feb 12, 2013 4:20 pm
by s.dot
I need help writing a regex for apache .htaccess

I want the path to be passed to index.php?path=$1. I need this not to apply to files ending in .css, .js, .jpg, .gif, .png. Also this doesn't need to apply to index.php.

For example:
domain.tld/Psuedo-Directory-Name/Sub-Name, should be passed to index.php?path=Psuedo-Directory-Name/Sub-Name

I have tried this:
[text]RewriteEngine On
RewriteRule index.php index.php [L]
RewriteRule ^.+ /index.php?path=$1 [L][/text]

I'm sure that's loads wrong and I can do it in one rule.

Re: help with apache rewrite rule

Posted: Tue Feb 12, 2013 4:40 pm
by requinix
You can use a negative lookahead to make sure the pattern doesn't start with "index.php", then another near the end for the file extensions.

Code: Select all

RewriteRule ^/?(?!index\.php)(.*\.(?!css|js|jpg|gif|png)[^.]+)$ index.php?path=$1 [L]
...if you really want just the one Rule. Otherwise a couple Conds makes it much easier to read.

Code: Select all

RewriteCond %{REQUEST_FILENAME} !\.(css|js|jpg|gif|png)$
RewriteCond %{REQUEST_FILENAME} !=%{DOCUMENT_ROOT}/index.php
RewriteRule ^/?(.*) index.php?path=$1 [L]
Or if you just want index.php to handle anything that doesn't exist,

Code: Select all

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/?(.*) index.php?path=$1 [L]

Re: help with apache rewrite rule

Posted: Tue Feb 12, 2013 4:47 pm
by s.dot
That answer is mind boggling. I thank you so much!

I'm making a just-for-fun site that I don't want to put much time into setting it up, but I want pretty urls. So that means I'm not going to use a framework and I'm not going to build my own url loader and not going to use a bunch of rewrites for specific pages.

I'll let index.php handle everything.

Re: help with apache rewrite rule

Posted: Tue Feb 12, 2013 5:08 pm
by s.dot
So now with the following script I can rewrite
domain.tld/category/pagename to index.php?path=category/pagename
and use category_pagename.php to load the page.

I'll have a single directory with all of the pages.

Code: Select all

if (!empty($_SERVER['REQUEST_URI']))
{
	if ($_SERVER['REQUEST_URI'] == '/' || $_SERVER['REQUEST_URI'] == '/index.php')
	{
		echo 'index';
		//require_once 'index_content.php';
		exit;
	}
	
	if (!empty($_SERVER['REQUEST_URI']))
	{
		//replace beginning and trailing slashes
		$fileName = substr($_SERVER['REQUEST_URI'], 1);
		$fileName = substr($_SERVER['REQUEST_URI'], -1) == '/' ? substr($fileName, 0, strlen($fileName) - 1) : $fileName;
		
		//replace slashes with underscores
		$fileName = strtolower(str_replace('/', '_', $fileName));
		
		if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/' . $fileName . '.php'))
		{
			require_once $_SERVER['DOCUMENT_ROOT'] . '/' . $fileName . '.php';
			exit;
		} else
		{
			echo '404';
			//send 404 header
		}
	}
}
Is there anything wrong with this simplistic approach? It's the first time I've done it.

Re: help with apache rewrite rule

Posted: Tue Feb 12, 2013 5:44 pm
by requinix
If you're not using the ?path= then the RewriteRules can be simplified a bit. Respectively, and making sure to keep any RewriteConds,

Code: Select all

RewriteRule ^/?(?!index\.php).*\.(?!css|js|jpg|gif|png)[^.]+$ index.php [L]

RewriteRule ^ index.php [L]

RewriteRule ^ index.php [L]
There are a couple comments I can make:
- The REQUEST_URI will always be present so you don't have to worry about that (unless you're using this for CLI scripts too).
- It will include query strings so you need to account for that. I personally like using strtok: it's basically a combination of substr() and strpos() at the same time, even excels if you need to chain a few of those together.

Code: Select all

$path = strtok($_SERVER["REQUEST_URI"], "?");
At the very least make sure that query string doesn't get into the $fileName.
- trim is a really easy way of removing leading and trailing characters. It will remove all of them and not just one from each end, turning "/foo///" into "foo", but that's correct in this case.
- I suggest a strict "only contains letters, numbers, and underscores" check on that filename, just to be extra cautious.

By the way, going to "/index" would trigger index.php and include itself recursively. To be absolutely sure that doesn't happen, I'd include a specific check on $fileName - in case other methods you have in place (ie, the URL rewriting and the ==/index.php check earlier) don't catch the problem beforehand.

Re: help with apache rewrite rule

Posted: Tue Feb 12, 2013 8:34 pm
by s.dot
All good points. Duh I knew that about trim (face Palm). Yes I gotta remove the query string from possibly being in file name. In some instances though I will want it. Just gotta make sure it's not trying to look for a file named it. I will do a check for numbers, letters, and -and _.

I feel clever for coming up with this very simple method of url management though I bet it's been done a thousand times before, lol.

Re: help with apache rewrite rule

Posted: Tue Feb 12, 2013 10:15 pm
by s.dot
Yeah, I'm not using the path part so I removed that from the .htacess.

So with the above mentioned points, here's my final little script:

Code: Select all

if (!empty($_SERVER['REQUEST_URI']))
{
	if ($_SERVER['REQUEST_URI'] == '/' || $_SERVER['REQUEST_URI'] == '/index.php' || $_SERVER['REQUEST_URI'] == '/index')
	{
		echo 'index';
		//require_once 'index_content.php';
		exit;
	}
	
	
	//set filename
	$fileName = $_SERVER['REQUEST_URI'];
		
	//replace query string, if any
	if (strpos($fileName, '?') !== false)
	{
		$fileName = preg_replace("#\?.+#", '', $fileName);
	}
		
	//replace beginning and trailing slashes
	$fileName = trim($fileName, '/');
		
	//replace slashes with underscores
	$fileName = strtolower(str_replace('/', '_', $fileName));
		
	//check for appropriate characters and file exists
	if (preg_match('#[a-zA-Z0-9_-]#', $fileName) && file_exists($_SERVER['DOCUMENT_ROOT'] . '/' . $fileName . '.php'))
	{
		require_once $_SERVER['DOCUMENT_ROOT'] . '/' . $fileName . '.php';
		exit;
	}
}

//if we reach this point, we send a 404 header
header('HTTP/1.0 404 Not Found');
require_once '404.php';
exit;