I want to create custom post types with multiple views based on URL.

E.g., suppose I have a "Product" post type. I would like to have something like the following:

I would be fine with this as well:

Is it possible to represent a page/post as different URLs, each with a different view of the same data? Would this require a lot of modification?

2 Answers 2


What you want is a rewrite endpoint.

You're already familiar with these. WordPress' urls that end with feed are simple a rewrite endpoint, and you can add your own!

add_rewrite_endpoint takes two arguments: the endpoint you want, and the "mask" of where you want it to go. "mask" is just a number that you can do some bitwise math with to specify where you want the endpoint to go. As of WP 3.4 you can specify a custom ep_mask when registering a post type.

The basic gist: hook into init, call add_rewrite_endpoint, specify the custom EP mask of your post type. Here's a really quick example (as a plugin here):

class WPSE45713
    const EP = 262144;

    private static $ins = null;

    public static function instance()
        is_null(self::$ins) && self::$ins = new self;
        return self::$ins;

    public static function init()
        add_action('plugins_loaded', array(self::instance(), '_setup'));
        register_activation_hook(__FILE__, array(__CLASS__, 'activate'));
        register_deactivation_hook(__FILE__, array(__CLASS__, 'deactivate'));

    public static function activate()

    public static function deactivate()

    public function _setup()
        add_action('init', array($this, 'register'));
        add_action('init', array($this, 'endpoints'), 15);
        add_filter('request', array($this, 'filter_request'));

    public function register()
        register_post_type('product', array(
            'label'     => __('Products', 'wpse'),
            'public'    => true,
            'rewrite'   => array(
                'ep_mask'   => self::EP,

    public function endpoints()
        add_rewrite_endpoint('detailed', self::EP);

    public function filter_request($vars)
            $vars['detailed'] = true;

        return $vars;

The only downside to this is that endpoints are meant to be used as /<endpoint_name>/<some_value>. So without <some_value> present the endpoint technically wouldn't work -- hence the filter hooked into request. Check if the endpoint is set, and set it to true if it is.

That also means that /detailed/asdf would work just as well as /detailed/. Not a huge issue to but something to be aware of.

On the front end side of things, you can check to see if the endpoint exists by using get_query_var:

    // we're on /product/some-product/detailed/
    // do stuff!

add_rewrite_endpoint adds query variables with the same name as you specified for the endpoint. detailed in this case.


Probably not the best way to do it, but one way to get there is: set your CPT to hierarchical, so it will allow parents, and allow it to have templates using either this, this or another approach.

Once you've set up things this way, organize your CPTs so that each one is parent to the "detailed", "summary" and "xml" sub-pages (i'm almost certain you can do this automagically, hooking to the saving post hook which name i can't rememember right now).

With that, you can just adjust templates for each "kind" of subpage (you may want to id them with custom values) so that they collect the info the way you want it.

This site is temporarily in read-only mode and not accepting new answers.

Not the answer you're looking for? Browse other questions tagged .