Dealing with static pages v2 (or… 3?)

Over the years of cake development we’ve seen a number of ways to get rid of the the /pages/ path in the URL for static pages.

By default if you create an “about us” page, such as in app/views/pages/about.ctp, the resulting URL would be:

I’m sure you’ve seen a ton of complaints and solutions about how to get rid of this seemingly “annoying” /pages/
To keep the URL’s clean, most people would obviously prefer to have instead. (No /pages/ in sight).

Recently, my favorite solution has become the following setting in the routes.php:

$staticPages = array(

$staticList = implode('|', $staticPages);

Router::connect('/:static', array(
    'plugin' => false,
    'controller' => 'pages',
    'action' => 'display'), array(
        'static' => $staticList,
        'pass' => array('static')

Go ahead, create your about.ctp and then attempt to access it by going to

The Router will nicely remove the /pages/ from the URL.

Why keep the pages in the array() and then use implode()? Couldn’t I just have the following?

$staticList = 'about|legal|policy|something';

You sure could, but array structure allows to keep things organized neater and if you have a large amount of pages it’s easier to scan visually to insert/remove pages as necessary. Either way, you have both options to work with.

  • Now that’s friggin brilliant!

  • teknoid


    Thanks. Although, I am only partially responsible for the final solution. I’m sure the initial idea came from IRC or some other place “out there”…

  • berclausen

    nice one :) I use an extremely similar approach for my apps!

    Sometimes another way (without dealing with pages controller at all, or even Cake) should be using app/webroot itself

    Moving .php files there and slightly modify .htacces for the redirection

    Redirect 301 /about /about.php

    or using rewrites

    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !(/)$
    RewriteRule ^(about|legal|policy|something)$ $1.php?%{QUERY_STRING} [L]

    of course you lose reverse Router’s magic for link building and blabla, it depends on the app, if every bit of gain is needed, this can help

  • teknoid


    Nice. Thanks for sharing. It is definitely better not involve the framework overhead, if possible.

  • Pingback: Dealing with static pages v2 (or… 3?) |

  • echo $this-Html->link(__(‘Policy’, true), array(‘controller’=>’pages’, ‘action’=>’display’, ‘static’=>’policy’));

    A link with the HTML helper looks like this, right?

  • teknoid


    That’s fine (although $this-> is missing “>”)… A simpler version would be:
    echo $this->Html->link(__(‘Policy’, true), ‘/policy’);

  • Pingback: CakePHP : signets remarquables du 11/03/2011 au 28/03/2011 | Cherry on the...()

  • Thats an interesting way of doing this, i like it alot!

  • Pingback: Static pages in CakePHP | Geoffrey Garbers()

  • This is super useful. Thanks for the post!

    I’ve just written my own post (referencing yours), that takes the automation of your suggestion a little further.

    You can read it, and find the code for it at

  • teknoid


    Glad you’ve found this useful.
    … and thanks for sharing your solution, nicely done.

  • Jparker

    I changed a bit Geoff’s code to have it work with folders hierarchy
    Here is my contrib :

    Hi there,

    I found teknoid post, and then your answer. I liked yours because of the automation thing and that I’m currently working on a project that has a lot of static pages.

    I adapted it to have it working with folders hierarchy in pages folder.

    The code is not the cleanest, but it works…
    Here is the code :

    $folder = new Folder();
    $available = $folder->tree( VIEWS . ‘pages’ . DS, true );
    $availableFolder = $available[ 0 ];
    $availablePages = $available[ 1 ];
    if( $availablePages ) {
    $extensions = array_pad( array( ), count( $availablePages ), ‘.ctp’ );
    $availablePages = array_map( ‘basename’, $availablePages, $extensions );
    if( $availablePages ) {
    if( $availableFolder ) {
    foreach( $availableFolder as $key => $folder ) {
    $availableFolder[ $key ] = substr( $folder, strlen( VIEWS . ‘pages’ . DS ) );
    ‘/:folder/:page.html’, array( ‘controller’ => ‘pages’, ‘action’ => ‘display’ ), array(
    ‘folder’ => implode( ‘|’, $availableFolder ),
    ‘page’ => ‘(?i)’ . implode( ‘|’, $availablePages ),
    ‘pass’ => array( ‘folder’, ‘page’ )
    Router::connect( ‘/:page.html’, array( ‘controller’ => ‘pages’, ‘action’ => ‘display’ ), array( ‘page’ => implode( ‘|’, $availablePages ), ‘pass’ => array( ‘page’ ) ) );

    As said on Geoff’s blog, that’s not the cleanest code… But it works. A custom router class should probably be more appropriate…

%d bloggers like this: