0

I have a site that I'm working on, but I'm annoyed that I have to work with ugly URLS. So, I have a URL of http://example.com/user.php?id=54 and another of http://example.com/foobar.php?name=Test.

How could I convert both of them to pretty URLS without adding it to .htaccess for every URL I want to make pretty?

example.com/user.php?id=54 => example.com/user/54

example.com/foobar.php?name=Test => example.com/foobar/Test

I have this in my .htaccess file:

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1\.php -f
RewriteRule ^([^/]+)/?$ $1.php [L]
RewriteRule ^$1/$3/? /$1.php?$2=$3 [NC]

Thanks, Lucy

My full .htaccess file:

# include classes on every page
php_value auto_prepend_file Resources/Classes.php

# add custom Directory Indexes
DirectoryIndex index.php Default.php Down.php

# begin routing to pretty URLs
Options +FollowSymLinks
RewriteEngine On
RewriteRule ^/(?!Resources)([0-9a-zA-Z-]+)/([0-9]+) /$1.php?id=$2 [NC]
RewriteRule ^/(?!Resources)([0-9a-zA-Z-]+)/([a-zA-Z-]+) /$1.php?name=$2 [NC]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*?)/?$ $1.php [L]
  • 1
    You should rewrite all request to index.php and then include file that you want. Check out this http://www.kratedesign.com/blog/2010/03/php-router-and-clean-urls/ – Mateusz Nowak Jul 20 '14 at 10:25
  • I'd prefer to use .htaccess, but thanks. – Lucy Brockleworth Jul 20 '14 at 10:26
  • @estshy: just reading your comment now, but that is actually very similar to what my answer suggests :) and I guess Lucy already negated all the work put into my answer :( – vol7ron Jul 20 '14 at 15:02
  • My two cents here, but I would like to emphasize that @estshy's comment raises an extremely important point. URLs and routing are an integral part of your application, one that should **not** be the responsibility of the web server. Just imagine that the application might some day be run on NGinx or Lighttpd, which do not support .htacess files at all. Moreover, mod_rewrite is a complicated beast and using these directives in .htaccess files is a performance killer. – SirDarius Jul 20 '14 at 15:10
  • @SirDarius I feel similarly. I also feel there are a lot of benefits to having the server handle it, since its demonized and should be quicker to process (and built into Apache). Regardless, it's nice to have control over the path as a developer and not have to depend on the web sys admin to have `.htaccess` and `mod_rewrite` enabled — imagine you're dependent on someone that one day decides they need to conserve resources or improve security and flip to switch to take it away; all the more reason to build it into the router. I like it most for visibility; your logic is not in a hidden file. – vol7ron Jul 20 '14 at 17:18

2 Answers2

0

Try this

Options +FollowSymLinks
RewriteEngine On
RewriteRule ^user/([0-9]+) /user.php?id=$1 [QSA,L]
RewriteRule ^foobar/([0-9a-zA-Z-]+) /foobar.php?name=$1 [QSA,L]

if you want global rule you can make

Options +FollowSymLinks
RewriteEngine On
RewriteRule ^([0-9a-zA-Z-]+)/([0-9a-zA-Z-]+) /$1.php?parameter=$2 [NC]

or more specifically

Options +FollowSymLinks
RewriteEngine On
RewriteRule ^([0-9a-zA-Z-]+)/([0-9]+) /$1.php?id=$2 [NC]
RewriteRule ^([0-9a-zA-Z-]+)/([a-zA-Z-]+) /$1.php?name=$2 [NC]

when argument will be a string it will pas name parameter and when argument will be integer there will be id parameter passed.

Mateusz Nowak
  • 4,021
  • 2
  • 25
  • 37
  • Works fine, but I did say "How could I convert both of them to pretty URLS without adding it to .htaccess for every URL I want to make pretty?". – Lucy Brockleworth Jul 20 '14 at 10:30
  • You will have to make some rules to every url if you want to have your application safe. – Mateusz Nowak Jul 20 '14 at 10:32
  • As I'm running it on a local server, with me being the only user, I'm not bothered about extreme safety much. – Lucy Brockleworth Jul 20 '14 at 10:35
  • Is there anything I could use instead of parameter to make it work with any named parameter? – Lucy Brockleworth Jul 20 '14 at 11:26
  • I've added 2 rules for 2 different parameters (id for integers and name for strings). Have you got more parameters? If yes, how apache will know what parameter should be pass for given URL? Eventually you can pass all parameters each time. I need more information. – Mateusz Nowak Jul 20 '14 at 11:36
  • I'm talking about the /$1.php?parameter=$2 part. Where it says ?parameter=, would I be able to substitute that to make it would without defining the name of the parameter? – Lucy Brockleworth Jul 20 '14 at 11:38
  • Nope. You need to know in some way the name of the parameter first. With one global rule we don't know whether id will be `id`, `name`, `order`, `foobar` or something much different. Try my third solution. – Mateusz Nowak Jul 20 '14 at 11:41
  • Snap! That's messed up my project. Hopefully, I can find an alternative method. – Lucy Brockleworth Jul 20 '14 at 11:45
  • Then, create one rule for each page or better own router class and your project will be working fine. – Mateusz Nowak Jul 20 '14 at 11:47
  • I've encountered a problem - when I go to my stylesheet (http://example.com/Resources/CSS/Main.css), it gives me a 404 error saying "The requested URL /Resources.php was not found on this server.". Any patches? – Lucy Brockleworth Jul 20 '14 at 14:00
  • That's way global rules sucks. You can create own rules for each url or exclude 'Resources' word in regexp. – Mateusz Nowak Jul 20 '14 at 14:10
  • How could I exclude it in Regexp? – Lucy Brockleworth Jul 20 '14 at 14:11
  • Check out http://stackoverflow.com/questions/2078915/a-regular-expression-to-exclude-a-word-string – Mateusz Nowak Jul 20 '14 at 14:13
  • See my updated post for my new .htaccess. That lets me use the CSS, but when I go to use the global rules on line 11/12, it gives me a 500 error. – Lucy Brockleworth Jul 20 '14 at 14:20
0

I may delete this answer in the future as it might be specific to my setup.


I recently discovered, using Apache, that anything after the URL was populating the PATH_INFO environment variable. This means that given your example, example.com/user/54, if user was a script the server could process, anything after it would be populated into PATH_INFO; in this case it would look like /54. This is a great find because with proper structure, you could make your own router similar to Rails.

I would create some landing page (e.g., index) which is going to be your application router: example.com/index/<model>/<id>/. Inside index would be your routing code. I'll use Perl to demonstrate, since it's better than PHP :) Note that index could be called anything that Apache can process (e.g., router.php, index.pl, application.rb); though, removing the extension adds to the beauty of the URL.

index:

#!/usr/bin/perl
use 5.012;

# Retrieve what you're looking for; obviously not production-ready
my ($model,$id) = $ENV{PATH_INFO} =~ m{^/([^/]+?)/([^/]+)};

# route the request
given($model){
   when('user'){ callUser($id); }     # callUser defined elsewhere, perhaps another script
   when('foobar'){ callFoobar($id); } # callFoobar defined elsewher, perhaps another script
   default { makePageDefault(); }
}

The script above is not production ready because it doesn't perform any sanitation and doesn't handle all the use cases you will need. My guess is you want something similar to Rails (e.g., example.com/movie/1/edit). While Apache is designed to handle the routing for you, there is some convenience in being able to manage this close to where your application code lives.

I have not implemented this method, so I'm curious to hear if this is something used and if there's any reason not to trust it.

vol7ron
  • 40,809
  • 21
  • 119
  • 172