Blog URL Rewrites in Perch CMS

by Iain Fergus

I love Perch. It's my CMS of choice for client work, so when it came to creating this blog it was the obvious choice. It's small, but powerful, and in particular I love the modular nature. If you don't need a Blog, there is no Blog app. So it's perfect for client sites as there are no unnecessary elements to distract in the admin section.

The other thing I love about Perch, is that it doesn't make any design assumptions or tie you to specific templates. While there are example templates provided for the Blog and other apps (such as the Gallery) you can completely customise it to suit your (or your client's) needs.

Now the only drawback of this is that Perch can't anticipate your folder/file structure and therefore doesn't provide Clean URLs out of the box*. While you need to do this yourself, Perch do provide solution examples to point you in the right direction, and the support on their official forum is top notch.

*Perch have recently released Perch Runway for more complex sites that I do believe offers routing options, but I've yet to try this out myself.

Over the last few years I have tried out different rewrite settings and page structures, but the one I've settled on for this blog is the one I'm likely to stick with.

Now I'm no expert with .htaccess rewrites but the following seem to work for me. I'm sharing them here as reference for anyone getting started with Perch, but won't be going into detail on all the page templates themselves, or indeed the basic set up of Perch.

Standard URLS

Assuming you have installed the Blog app, and have the blog pages within a folder /blog you will have URLs that look like these:

/blog/archive.php (all posts unfiltered)
/blog/archive.php?cat=category-name (posts in a category)
/blog/archive.php?tag=tag-name (posts in a category)
/blog/archive.php?year=2015 (posts in a year)
/blog/archive.php?year=2015&month=02 (posts in a month)
/blog/archive.php?author=iain-fergus (posts by author)
/blog/post.php?s=2015-02-28-test-post (specific post)
/blog/rss.php (rss feed)

These will of course work fine, but it's often desirable to have clean URLs as they are preferred by search engines, users and lets be honest they just look nicer.

It's also worth noting that each of the above (excluding the rss feed) may have page or preview parameters added that we also need to take into account.

For example:

Desired URLS

If your website is on an Apache server you can take advantage of mod_rewrite in your .htaccess file to rewrite the above URLS to the following:


…and the page and preview URLS could be rewritten to these:


.htaccess Rewrites

To achieve the URLs above, these are the rewrite rules I have used in my .htaccess file. These have been adapted from those provided in the Perch solutions documentation. I've added the first rule to deal with the multiple pages of the unfiltered /blog/archive pages, and rules 4 and 5 to handle the author archive pages.

<IfModule mod_rewrite.c>
    RewriteRule ^blog/archive/page:([0-9]+)/{0,1}$ /blog/archive.php?page=$1 [L]
    RewriteRule ^blog/archive/category:([a-zA-Z0-9-/]+)/page:([0-9]+)/{0,1}$ /blog/archive.php?cat=$1&page=$2 [L]
    RewriteRule ^blog/archive/category:([a-zA-Z0-9-/]+)$ /blog/archive.php?cat=$1 [L]    
    RewriteRule ^blog/archive/author:([a-zA-Z0-9-/]+)/page:([0-9]+)/{0,1}$ /blog/archive.php?author=$1&page=$2 [L]
    RewriteRule ^blog/archive/author:([a-zA-Z0-9-/]+)$ /blog/archive.php?author=$1 [L]
    RewriteRule ^blog/archive/date:([a-zA-Z0-9-/]+)/page:([0-9]+)/{0,1}$ /blog/archive.php?year=$1&page=$2 [L]
    RewriteRule ^blog/archive/date:([a-zA-Z0-9-/]+)$ /blog/archive.php?year=$1 [L]
    RewriteRule ^blog/archive/date:([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)/page:([0-9]+)/{0,1}$ /blog/archive.php?year=$1&month=$2&page=$3 [L]
    RewriteRule ^blog/archive/date:([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)$ /blog/archive.php?year=$1&month=$2 [L]

    RewriteRule ^blog/archive/tag:([a-zA-Z0-9-/]+)/page:([0-9]+)/{0,1}$ /blog/archive.php?tag=$1&page=$2 [L]
    RewriteRule ^blog/archive/tag:([a-zA-Z0-9-/]+)$ /blog/archive.php?tag=$1 [L] 

    RewriteRule ^blog/([a-zA-Z0-9-/]+)/preview$ /blog/post.php?s=$1&preview=all [L]

    RewriteRule ^blog/rss.xml$ /blog/rss.php [L]

    # Redirect to PHP if it exists.
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME}.php -f 
    RewriteRule ^(.+)$ $1.php [L,QSA]

    ## This needs to be below the catch all, otherwise blog/archive -> blog/archive.php fails
    RewriteRule ^blog/([a-zA-Z0-9-/]+)$ /blog/post.php?s=$1 [L]

One important thing to note is that the order of the rules is important. When RewriteRule ^blog/([a-zA-Z0-9-/]+)$ /blog/post.php?s=$1 [L] is placed anywhere else but below the catch all (for removing the .php extension) the blog/archive to blog/archive.php rule seems to fail.

If anyone knows why this is the case, please let me know and I will update this post for others to see.

Perch Templates

As well as updating the "Blog post page path" in the Perch Admin settings to be /blog/{postSlug} instead of /blog/post?s={postSlug} you will need to update your templates in /perch/templates/blog so that all archive and post urls are updated to the new format.

Depending on your setup you may have to update the following files:

  • author_list.html
  • category_link.html
  • months_months_link.html
  • months_year_link.html
  • post_category_link.html
  • post_in_list.html
  • post_tag_link.html
  • tag_link.html
  • year_link.html

Most are pretty straight-forward but the one worth looking at further is the post_in_list.html template that is used to display the posts on archive.php.

As this page displays and filters posts based on many parameters and can often require pagination these all need to be addressed so that the page urls with page numbers are generated correctly.

This can be done using Perch's Tag Rewrite Attribute

Here is the pagination set up in my post_in_list.html

<perch:if exists="paging">
        <div class="paging">
            <div class="paging-prev">
            <perch:if exists="not_first_page">
                <a href="<perch:blog id="prev_url" rewrite="/blog/archive{cat:/category:%s}{tag:/tag:%s}{year:/date:%s}{month::%s}{page:/page:%s}" type="hidden" encode="false" />">&laquo; Previous</a>
            <div class="paging-count">
            <span class="page">Page</span>
<perch:blog id="current_page"/> of <perch:blog id="number_of_pages" />
            <div class="paging-next">
            <perch:if exists="not_last_page">
                <a href="<perch:blog id="next_url" rewrite="/blog/archive{cat:/category:%s}{tag:/tag:%s}{year:/date:%s}{month::%s}{page:/page:%s}" type="hidden" encode="false" />">Next &raquo;</a>

The important bits to notice above are the rewrite attributes on the prev_url and next_url tags.

<perch:blog id="prev_url" rewrite="/blog/archive{cat:/category:%s}{tag:/tag:%s}{year:/date:%s}{month::%s}{page:/page:%s}" type="hidden" encode="false" />">

<perch:blog id="next_url" rewrite="/blog/archive{cat:/category:%s}{tag:/tag:%s}{year:/date:%s}{month::%s}{page:/page:%s}" type="hidden" encode="false" />">

These make sure that the pagination URLs are rewritten in the template based on the category or tag etc and output to the page in place of the full query string.

In Summary

This is a general look at what can be done with URL rewriting of the Perch Blog app. Hopefully this will help save some time for anyone that is just getting started with Perch or it's Blog app. If you don't like the particular rewrites and separators that I've chosen, I'm sure the above can be altered to suit you without too much work.

Iain Fergus

Posted by: Iain Fergus

I'm a Web & Print Designer based in Ayr, Scotland. This blog is a collection of my personal ramblings.

Feel free to get in touch with me on Twitter @darkflare or drop me an email iain[at]

Posted in: Web Development

Tagged with: , &