ForceType for nice URLs with PHP

This has been covered before, but I was just setting up a new force type on our servers and thought I would mention it for the fun of it. You see lots of stuff about using mod_rewrite to make friendly URLs or SEO friendly URLs. But, if you are using PHP (and I guess other Apache modules) you can do it without mod_rewrite.  We have been doing this for a while at dealnews.  Even before SEO was an issue.

Setting up Apache

From the docs, the ForceType directive “forces all matching files to be served as the content type given by media type.” Here is an example configuration:

<Location /deals>
ForceType application/x-httpd-php
</Location>

Now any URL like http://dealnews.com/deals/Cubicle-Warfare/186443.html will attempt to run a file called deals that is in your document root.

Making the script

First save a file called deals witout the .php extension. Modern editors will look for the <?php tag at the first and will color it right. Normally you take input to your PHP scripts with the $_SERVER["QUERY_STRING"] or the $_GET variables. But, in this case, those are not filled by the URL above. They will still be filled if there is a query string, but the path part is not included.  We need to use $_SERVER["PATH_INFO"]. In the case above, $_SERVER["PATH_INFO"] will be filled with /Cubicle-Warfare/186443.html. So, you will have to parse the data yourself. In my case, all I need is the numeric ID toward the end.

$id = (int)basename($_SERVER["PATH_INFO"]);

Now I have an id that I can use to query a database or whatever to get my content.

Avoid “duplicate content”

The bad part of my use case is that any URL that starts with /deals/ and ends in 186443.html will work. So, now we have duplicate content on our site. You may have a more exact URL pattern and not have this issue.  But, to work around this in my case, we should verify that the $_SERVER["PATH_INFO"] is the proper data for the content requested. This code will vary depending on your URLs. In my code, I generate the URL for the content and see if it matches. Kind of a reverse lookup on the URI.  If it does not match, I issue a 301 redirect to the proper location.

header("HTTP/1.1 301 Moved Permanently");
header("Location: $new_url");
exit();

Returning 404

Now, you have to be careful to always return meaningful data when using this technique. Search engines won’t like you if you return status 200 for every possible random URL that falls under /deals. I know that Yahoo! will put random things on your URLs to see if you are doing the right thing. So, if you get your id and decide this is not a valid URL, you can return a 404.  In my case, I have a 404 file in my document root.  So, I just send the proper headers and include my regular 404 page.

header('HTTP/1.1 404 Not Found');
header('Status: 404 Not Found');
include $_SERVER["DOCUMENT_ROOT"]."/404.html";
exit();

About these ads

12 Responses to ForceType for nice URLs with PHP

  1. [...] You can read the rest of this blog post by going to the original source, here [...]

  2. Vid Luther says:

    Brian, Interesting post.. but some questions..

    1. Why not use mod_rewrite?
    2. What about SetHandler (http://httpd.apache.org/docs/1.3/mod/mod_mime.html#sethandler) ? Is it better to use forcetype ?

    3. What do you do when a url is like /deals/41/apple/45.5./d.html (not sure why, but just in case a bad request like this came in?)

  3. doughboy says:

    > 1. Why not use mod_rewrite?

    Well, “back in the day” we were all about keeping our Apache processes as slim as possible. At that time, mod_rewrite added a lot of memory footprint. This adds none.

    As for why not now? Because I don’t have to. mod_rewrite is really powerful. And like anything that is really powerful, its complicated.

    > 2. What about SetHandler

    I have tried using SetHandler with PHP before and not had success. If someone has a solution, I would be interested. I am not sure one would be better than the other.

    > 3. What do you do when a url is like /deals/41/apple/45.5./d.html
    > (not sure why, but just in case a bad request like this came in?)

    We would return a 404 on that. Try it for yourself. http://dealnews.com/deals/41/apple/45.5./d.html That was what the 404 part of the post covered.

  4. laZee says:

    Sounds interesting, i tried it but it didn’t work. My file wasn’t interpreted as PHP, it just showed the code in my browser. And even this was showed only when i was requesting http://www.domain.com/filename, not when requesting http://www.domain.com/filename/something/else.

    I don’t know why, maybe special conditions are defined for by my webhoster…

  5. Brian Moon’s Blog: ForceType for nice URLs with PHP

  6. doughboy says:

    @laZee

    Are you running PHP as fasxtcgi or cgi? This method would require mod_php

  7. [...] his blog today, Brian Moon talks about setting up ForceType directives in Apache, specifically how to make [...]

  8. > I have tried using SetHandler with PHP before and not had success. If someone has
    > a solution, I would be interested. I am not sure one would be better than the other.

    @doug

    I have sucessfully combined Sethandler with PHP and mod_rewrite — this last one issues a 403 error when it cannot find a directory or a relevant file. Please find below the needed configuration snippets.

    I’m not sure however if this setup does handle “duplicate content” the way it should: all I wanted from my clean URL scheme was to handle trailing slash as well as extra parameters in PATH_INFO variable and not trigger any HTTP error.

    Hope this helps!

    =BEGIN vhost.conf

    Options All -Indexes

    DirectoryIndex index

    Action php-script /php-bin/handler.php
    SetHandler php-script

    RewriteMap uri txt:/usr/local/www/vhosts/domain/uris.txt

    ## 28-Feb-2007 :: reinject %{PATH_INFO} into %{REDIRECT_PATH_INFO} :-)
    ## 27-Mar-2007 :: make sure we don’t trigger mode_rewrite for specified locations
    RewriteEngine on
    RewriteCond ${uri:%{REQUEST_URI}|STAT} =STAT
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^.+ – [C,E=PATH_INFO:%{PATH_INFO}]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^.+ – [F,L]

    =END vhost.conf

    =BEGIN uris.txt
    ##
    ## Apache/mod_rewrite selection table
    ##

    /hello/world -

    =END uris.txt

    =BEGIN handler.php
    <?php

    /* {{{ path info fix */
    if (isset($_SERVER['REDIRECT_URL'])
    && isset($_SERVER['REDIRECT_PATH_INFO'])
    && $_SERVER['REDIRECT_STATUS']
    =END handler.php

  9. Oops, handler.php snippet did not pass thru (!?)

    /* {{{ path info fix */
    if (isset($_SERVER['REDIRECT_URL'])
    && isset($_SERVER['REDIRECT_PATH_INFO'])
    && $_SERVER['REDIRECT_STATUS'] < BAD_REQUEST) {
    $length = -strlen($_SERVER['REDIRECT_PATH_INFO']);
    if (0 !== $length) {
    $_SERVER['REDIRECT_URL']
    = substr($_SERVER['REDIRECT_URL'], 0, $length);
    $_SERVER['PATH_TRANSLATED']
    = substr($_SERVER['PATH_TRANSLATED'], 0, $length);
    $_SERVER['PATH_INFO'] = $_SERVER['REDIRECT_PATH_INFO'];
    }
    else {
    unset($_SERVER['PATH_INFO']);
    }
    }
    /* }}} */

  10. [...] ForceType for nice URLs with PHP This has been covered before, but I was just setting up a new force type on our servers and thought I would mention it […] [...]

  11. Robert says:

    RE laZee and x.com/whatever

    Same thing here. With .php or .htm works fine otherwise just kicks back the script to the browser. That is, the server is not parsing the html or php. Godaddy claims that it is the Java installation that gets in the way, if I remove that from the server configuration (if it is not needed for my app), then it should work. I have not tried it yet, maybeeee? they are right but don t know yet! Hey, update me if you figure it out…

  12. Rek-port says:

    I think that using “mod_rewrite” is most right for this situation.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: