I would like to add a rewrite for only certain pages (not all pages) that would insert a base slug into the permalink. This is sort of a unique situation where we know all the page names beforehand and they will not change.

For example:




I am trying to use "add_rewrite_rule", but not having much luck. Below is one of my iterations and I have had many. Any guidance would be appreciated.

I understand that there is a plugin that allows you to totally change your permalinks of your post and pages to whatever you want, but was hoping I could do it programmatically myself.

add_action( 'init', 'page_change' );
function page_change()
        'top' );

Updated code below:

function new_rewrite_rule()
    add_rewrite_rule('^services/statictitle$', 'index.php?pagename=oldname', 'top');
add_action('init', 'new_rewrite_rule');

So this code allows the link to be accessible at 'services/statictitle.' However, the old link is still accessible as well. So I am not sure I am going about this in the correct way.....Any help would be appreciated.



Here is an alternative way that I think might be more suitable. Although, this targets all pages. Would there be a way to just target the pagename or id?

function custom_page_rules() {
  global $wp_rewrite;
  $wp_rewrite->page_structure = $wp_rewrite->root . 'services/%pagename%'; 
  • Adding rewrite rules doesn't change permalinks, it adds additional rules, that's why pages are still available at their original URL. All pages on the same level are captured by a single rewrite pattern, so changing specific pages is actually a bit challenging. Is there a reason why you can't do the simplest method of just creating a service page and making pages children of that page? – Milo Oct 28 '16 at 15:36
  • Hi. Milo. There is a plugin called Custom Permalinks that does this on a page to page basis. Basically, making the link whatever you want. However, it activates site wide, which i'd rather not do. The slug I want to insert into the general pages is reserved for a custom post type. The page template we want for this URL is completely different than the template used by the CPT or I was thinking about trying to pull in different template_parts based on a category or metafield. – Work-Together2013 Oct 28 '16 at 17:36
  • you can load whatever template you want for any content type with filters. – Milo Oct 28 '16 at 18:35

Asumption: You use Apache as your web server.

If you already know the page slugs and they won't change, just add them to your .htaccess file. It'll be much faster and less complicated.

Paste the following Redirect lines at the top of your .htaccess file.

Redirect 301 /pagetitle/ /service/pagetitle/
Redirect 301 /pagetitle2/ /another-dir/pagetitle2/

# BEGIN WordPress
<IfModule mod_rewrite.c>
  • Thanks for the response. I would like to learn how to do it as a function as im more familiar in this area. And I also just want to learn how it might be done. – Work-Together2013 Oct 28 '16 at 1:25
  • @Work-Together2013 fair enough. After you learn how it's done, I'd recommend the .htaccess method though for performance. :-) – cowgill Oct 28 '16 at 2:07
  • @cowgill, htaccess has cost and every single request, even for an image file takes more time because it needs to be read and processed. if you redirect happens only once a year, implementing it at wordpress might be more efficient. – Mark Kaplun Nov 3 '16 at 13:13

Please have a look on the code below:

add_rewrite_rule( 'services/([^/]*)/?','index.php?post_type='.$post_type.'&name=$matches[1]','top');

For better understanding of rewrite API please check the WP_REWRITE_API and Building Customized URLs in WordPress

Waiting for your feedback!

Hope it will work for you!

  • Hi. I am not looking to change all the permalinks for a given post type. I would like to target individual pages and insert a base slug on a case by case bases. Possibly by page name.... – Work-Together2013 Oct 28 '16 at 2:04

This approach might be a little more flexible.

$basePageRewrite->add_rule( 'service', 'page-title' );

At the end of the class definition you should be able to add a base and page. The URL rewrites will be constructed dynamically for the rules you add. If a page is called directly is will be redirected to the $base/$pageTitle.


if ( ! class_exists( 'BasePageRewrite' ) ):

    class BasePageRewrite {

        const ENDPOINT_QUERY_PARAM_REDIRECT = '____base_page_rewrite__redirect';

        private static $_hash_map = array ();

         * BasePageRewrite constructor.
        public function __construct() {
            add_filter( 'query_vars', array ( $this, 'add_query_vars' ), 0 );
            add_action( 'parse_request', array ( $this, 'sniff_requests' ), 0 );
            add_action( 'init', array ( $this, 'add_endpoint' ), 0 );

         * Add rewrite rules here.
         * @param string $base Base of URL
         * @param string $page Slug of page
        public function add_rule( $base, $page ) {
            if ( ! array_key_exists( $base, static::$_hash_map ) ) {
                static::$_hash_map[ $base ] = array ();

            if ( ! array_key_exists( $page, static::$_hash_map[ $base ] ) ) {
                static::$_hash_map[ $base ][ $page ] = 'name';

         * Add our custom query arg to check in `parse_request`.
         * @param $vars
         * @return array
        public function add_query_vars( $vars ) {
            $vars[] = static::ENDPOINT_QUERY_PARAM_REDIRECT;

            return $vars;

         * Add rewrite rules.
         *      page --> base/page
         *      base/page === page
         * Note:
         *      `flush_rewrite_rules()` is only added for testing.
        public function add_endpoint() {

            foreach ( static::$_hash_map as $base => $pages ) {

                foreach ( $pages as $pageName => $param ) {

                    // page --> service/page

                    add_rewrite_rule( "^$pageName/", 'index.php?' . static::ENDPOINT_QUERY_PARAM_REDIRECT . "=/$base/$pageName/", 'top' );

                    // service/page === page

                    add_rewrite_rule( "^$base/$pageName/", "index.php?$param=$pageName", 'top' );

            flush_rewrite_rules( true );  //// <---------- REMOVE THIS WHEN DONE TESTING

         * Redirect regular page to base/page,
         * @param $wp_query
        public function sniff_requests( $wp_query ) {

            global $wp;

            if ( isset( $wp->query_vars[ static::ENDPOINT_QUERY_PARAM_REDIRECT ] ) ) {

                // page --> service/page

                $redirect = $wp->query_vars[ static::ENDPOINT_QUERY_PARAM_REDIRECT ];
                $new_url  = site_url( $redirect );
                wp_redirect( $new_url, 301 );

    // Create the class
    $basePageRewrite = new BasePageRewrite();

    // Add individual rules
    $basePageRewrite->add_rule( 'service', 'page-title' );
    $basePageRewrite->add_rule( 'another-service', 'another-page-title' );

endif; // BasePageRewrite
  • This is interesting but getting the: Firefox has detected that the server is redirecting the request for this address in a way that will never complete. – Work-Together2013 Nov 1 '16 at 21:18
  • Could it be your urls end with / ? The regex is a little weak in that regard. – jgraup Nov 1 '16 at 21:35
  • Did you try with the trailing slash? – jgraup Nov 3 '16 at 13:00

The other part that you need to do here, it is to change the permalink of the page with the page_link filter or the_permalink filter.

here is a mirror question to your own - https://stackoverflow.com/questions/27432586/wordpress-page-link-filter

This will do the redirects because wordpress redirects to the canonical address when the url being accessed is different then the canonical one, and the canonical url for posts and pages is their permalink. As an added bonus you get everything else pointing to the right URL as well with no need of redirection at all.


I know you specifically asked for rewrite rules, but what about redirection? You can achieve the same end result and same user experience. You can change the hierarchy of your pages if you want so that


is available and


is something that gets redirected to the first address.

This skips your .htaccess altogether for simplicity. It looks like you actually want to learn how to do this as a rewrite rule, which is cool, but if you decide that it is being difficult and you just want a solution that works whether you learn this or not then there are a few different redirection plugins that can handle this for you. My personal favorite is titled "Redirection" and it can handle regex (with a little practice) and it also counts the number of times a redirect rule is used so that you can monitor if you even need to keep it in place.

Lastly, I mentioned in the beginning that you can change your page hierarchy to reflect what you want your URLs to look like and you should probably do this either way if you haven't already. If not then you might find yourself with an infinite redirect situation. (which is also something to keep in mind if you go the redirect plugin route as well as I've seen this happen easily with users who have a lot of redirects and they make a new one that conflicts with one that already exists.)

TLDR: outside the box answer. there are one or more plugins that can achieve this function without actually doing a rewrite.

  • redirection also doubles the work your server has to do to serve each request. – Milo Nov 3 '16 at 15:17
  • I'll admit that it can only add to the workload, but that is always a price to pay. I don't see it fully doubling the workload, but I have not specifically tested this. This should fix the issue you raised in a previous comment about having the other URLs still being available as those URLs will be redirected to the new URL (still linkable and functional, but not directly accessible). Ultimately the most perfect solution is whatever fits the OP's needs, but I chose to give a different answer to show alternative options. – KnightHawk Nov 3 '16 at 15:26

I've encountered a very similar situation to what seems to be your expectations.

We have a parent page "Our Services" (domain.com/services/) ...

We also have several child pages representing each service provided under the parent page "Our Services". For example: "Web Design" (domain.com/services/web-design/)

Now, we also have a "Portfolio". Instead of dedicating an entire top level section on the website for the portfolio, we decided that we wanted it underneath the main "services" section (domain.com/services/).

So for example, the main section of our portfolio was to be located here: domain.com/services/portfolio/

The portfolio is it's own custom post type, containing individual portfolio items.

So the URL to a specific portfolio item looked like this: domain.com/services/portfolio/sample-portfolio-item/.

When you register your custom post type, there's a configurable parameter for rewrite['slug']. This is where you would specify $args['rewrite']['slug'] = 'services/portfolio';

Of course, anytime you make changes to URL rewrite rules, you need to flush the rewrite rules. Programmatically you would use flush_rewrite_rules(); optionally for quick testing you could always just navigate to Dashboard -> Settings -> Permalinks since this automatically flushes the rewrite rules for you, simply just by visiting that admin page.

Hope you (or someone) finds the information in my answer useful and relevant.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.