Using mod_rewrite to make dynamic url "friendly"

Hello all,

I have never used mod_rewrite before but I have been advised by a marketing company my workplace uses tthat I need to rewrite my urls which appear as so:

mywebsite/insurance-news/news.php?id=800464237&title=building_stocks_plummet

into something more like:

mywebsite/insurance-news/id/800464237/title/building_stocks_plummet/

or

mywebsite/insurance-news/id/800464237/title/building_stocks_plummet.html

I have been trying for over 9 hours now to figure this out and have followed all the tutorials I can find with no luck, mod_rewrite is functional on my server as I created a test redirect as exlpained on one of the tutorials and it worked perfectly so I am not sure what I am doing wrong.

This is the link that brings the page up which creates the “non-friendly” urls I am trying to re-write

<a href=“news.php?id=<?php echo $row_BusinessInsuranceNews[‘newsArticleID’]; ?>&title=<?php echo str_replace(array(’ ‘, ’ ‘), ‘_’, preg_replace(’/[^a-zA-Z0-9 s]/’, ‘’,$row_BusinessInsuranceNews[‘htmlTitle’])); ?>”>more</a>

However, after numerous attempts at different rules when I click on it all I get is my dynamic URL:

mywebsite/insurance-news/news.php?id=800464237&title=building_stocks_plummet

These are the rules I have tried so far: Note: my .htaccess file is located in the correct directory and is headed up with:
Options +FollowSymlinks
RewriteEngine On

RewriteRule ^news/?([0-9]+)/([a-zA-Z_])/$ /news.php?id=$1&title=$2 [L]

RewriteRule ^news/([^/]+)/([^/]+)/?$ /news.php?id=$1&title=$2&size=$3 [L]

RewriteRule ^news/(.)/(.)/$ /insurance-news/news.php?id=$1&title=$2

RewriteRule news/id/(.)/title/(.)/ news.php?id=$1&title=$2
RewriteRule news/id/(.)/title/(.) news.php?id=$1&title=$2

I have tried all of the above and many variations in between - Can anyone help me out? Any advice would be greatly appreciated :frowning:

generally friendly urls work with single “access point”, like index.php. from that entry point you dispatch the request accordingly.

This may help in routing all the requests to a single script (index.php). from there you can parse the request and route it (exploding the request url with “/” is a good start).

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /test/

    #Removes access to the system folder by users.
    #Additionally this will allow you to create a System.php controller,
    #previously this would not have been possible.
    #'system' can be replaced if you have renamed your system folder.
    RewriteCond %{REQUEST_URI} ^system.*
    RewriteRule ^(.*)$ /index.php?/$1 [L]
    
    #When your application folder isn't in the system folder
    #This snippet prevents user access to the application folder
    #Submitted by: Fabdrol
    #Rename 'application' to your applications folder name.
    RewriteCond %{REQUEST_URI} ^application.*
    RewriteRule ^(.*)$ /index.php?/$1 [L]

    #Checks to see if the user is attempting to access a valid file,
    #such as an image or css document, if this isn't true it sends the
    #request to index.php
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index.php?/$1 [L]
</IfModule>

<IfModule !mod_rewrite.c>
    # If we don't have mod_rewrite installed, all 404's
    # can be sent to index.php, and everything works as normal.
    # Submitted by: ElliotHaughin

    ErrorDocument 404 /index.php
</IfModule> 

Mod rewrite translates a requested URL to another. It does not modify the URL in the address bar. So if the links your PHP is producing looks like mywebsite/insurance-news/news.php?id=800464237&title=building_stocks_plummet then mod rewrite is not going to change those links, or redirect the user if that’s in their address bar.

What it can do is make a request to news/building-stocks-plummet/800464237/ actually serve news.php?id=800464237&title=building-stocks-plummet

Ideally you’d avoid having the long numeric ID in the URL (800464237). But you can only get rid of it, if you can map the title string to a unique news item. This will be more work, as you have to store a unique URL slug with each article, so use in place of an ID.

Assuming you don’t go that far I’m assuming the news.php script does not need to receive title in the query string? The ID is enough to get the rest of the data from the database?

In which case this might work.


RewriteRule ^news/([a-zA-Z0-9\\.\\_\\-]+)/([0-9]+)/?$ news.php?id=$2

Then
news/my-awesome-title/3501

will request
news.php?id=3501

You need to change the links you output to be in the new form.
As I said Mod rewrite won’t change the hyperlinks you output. Only how those URLs are handled.

By the way, did the marketing company mention that if you just change a lot of your URLs, any page rank or search engine position the old pages (URLs) had will be largely wasted, and the new pages could appear as duplicate content?
You’ll need to setup a 301 redirect from the old to the new URLs to avoid this.

Try this one

RewriteRule ^insurance\-news/id/([0-9]+)/title/([a-zA-Z_])\.html$ news.php?id=$1&title=$2

<snip/>

Cranial Bore thanks so much for explaining that the link shouldn’t change if i don’t change the URL I am calling - after all the tutorials I read I never actually understood it because they all talk about changing the way a user sees a url. However it raises another query for me - when I am calling the newly written link how do I take out spaces and special characters like I do when I call the link now:

<a href=“news.php?id=<?php echo $row_BusinessInsuranceNews[‘newsArticleID’]; ?>&title=<?php echo str_replace(array(’ ‘, ’ ‘), ‘_’, preg_replace(’/[^a-zA-Z0-9 s]/’, ‘’,$row_BusinessInsuranceNews[‘htmlTitle’])); ?>”>more</a>

If this is what I call?

<a href=“news/building-stocks-plummet/800464237/”>more</a>

I am sorry for being such a dodo about this concept!

P.s Thanks for the additional not about the 301’s I haven’t gone live with the php news section of my site yet so there are no current URLs so I don’t think I’ll need any 301’s I am just in testing at the moment I have to go live in a week though and this is one of the last jobs I have to get done first.

also - if my data is dynamic and constantly changing with new news items - I won’t even be able to call that url will I - as I won’t know what it is?


/**
* Create URL safe title
* @param string $title
* @return string
*/
function title_slug($title) {
   $slug = strtolower(trim($title));
   $slug = preg_replace('/ +/', '-', $slug);
   $slug = preg_replace('/[^a-z0-9\\.\\,\\_\\-]/', '', $slug);

   return $slug;
}

Pass in your full text titles to that function. It will convert spaces to dashes, and then remove everything except for alpha numeric characters and . , _ -

This will be much tidier than doing the filtering inline.

I’d go a step further and write a function to return the HTML hyperlink from the link data. By doing this you can change the format of the link at any time, and the flow of your code will be easier to understand.


/**
* Build the href value for news links
* @param string $id
* @param string $title full unfiltered HTML title
* @return string
*/
function news_link($id, $title) {
   $slug = title_slug($title);
   return "news/$slug/$id/";
}

To use:


$href = news_link($row_BusinessInsuranceNews['newsArticleID'], $row_BusinessInsuranceNews['htmlTitle']);
echo "<a href='$href'>more</a>";

This would output a link that looks like this:


news/building-stocks-plummet/800464237/

news.php only needs to get the last segment (id). It doesn’t care what the only-the-fly URL slug was.

Make sense?

Oh that’s an excellent way of doing it! I’ll implement it and see how I go. Thanks so much for getting back to me so quickly!

Hi again Cranial Bore :slight_smile:

You are a genius! The whole thing worked perfectly, I just had to make the comma not allowed in the $slug as I actually had a title with a comma in it first up and it wouldn’t match as the mod_rewrite rule didn’t allow them.

The only problem I have now is that I have to make all my links on news.php absolute rather than relative as they won’t seem to work relatively anymore but that’s ok it’s just a one off page.

Thanks so much for all your help!!!

You can also use <base> in the head of your HTML.


<base href="/">

That way the browsers will know that they are actually in the root directory of your site, and not in the “fake” news directory.

Also, to add to cranial-bore’s excellent description of how things work, I also make a point of letting PHP check if the URL a user entered is correct.
For example if you have a news article, people link to it, and than you change the title, the old links will be “incorrect” (the title will be incorrect), but still work. Search engines might think it’s duplicate content, because you have the exact same content on two different URLs.

Assuming you already have the data, it would be something like


if (isset($_GET['title']) && $_GET['title'] !== title_slug($row_BusinessInsuranceNews['htmlTitle']))
{
   header('Location http://'.$_SERVER['HTTP_HOST'].'/'.news_link($row_BusinessInsuranceNews['newsArticleID'], $row_BusinessInsuranceNews['htmlTitle']), true, 301);
}

So for example if you have an article named “Building stocks plummet” with a URL news/building-stocks-plummet/800464237/ and then change the title to “Building stocks won’t stop plummeting” with a URL news/building-stocks-wont-stop-plummeting/800464237/ and people request the old URL, they will be 301 redirected to the new one.