Ask your WordPress questions! Pay money and get answers fast! Comodo Trusted Site Seal
Official PayPal Seal

Permalinks help WordPress

I'm having a heck of a time with permalinks. Seems like everything I change nothing matters. So I'm looking for some help.

So here's my setup. I have a custom post type that I've setup for a portfolio style website. I also have a custom taxonomy to label these portfolio items. My portfolio section is just called "work". And I have categories such as "print, web, multimedia". That sort of thing.

I want my urls to be "/work/category_name/post_name/". But no matter what I do I always get "/work/post_name/".

What am I doing wrong and what can I do to fix this? Thanks!

Answers (5)


Milan Petrovic answers:

WordPress for custom post types allows only one form of permalink that includes post type name (or custom slug) and post name.

My plugin GD Custom Posts And Taxonomies Tools Pro allows settings custom permalinks for each custom post type that can include taxonomies, date elements and other things, just like WordPress does for normal posts:

Plugin home:

Custom permalinks:



Fahad Murtaza answers:

In register_post_type('work', array(...

Did you use

'rewrite' => array("slug" => "work"), // Permalinks format

and did you use this permalink ?


You will need to save permalinks again with above settings.

Update: I think John has a way better solution.


John Cotton answers:

Hi Matt

This is doable, but not for faint hearted!

Firstly, you need to make sure your post type is declared correctly. If you doing that with a plugin, then this might be tricky. If you have code in functions.php then it's just a matter of changing the rewrite line to this:

'rewrite' => array( 'slug' => 'work/%taxonomy%', 'hierarchical' => true, 'with_front' => false )

Then you need to filter the permalink to create your custom version:

function filter_permalink( $permalink, $post, $leavename, $sample ) {
if( $post->post_type == 'work' ) {

if( $terms = get_the_terms($post->ID, 'YOUR_TAXONOMY_NAME') ) {
$dir = array();
$term = array_pop($terms);

$dir[] = $term->slug;

while( $term->parent != 0 ) {
$term = get_term( $term->parent, 'YOUR_TAXONOMY_NAME' );
$dir[] = $term->slug;

$dir = array_reverse( $dir );
$permalink = str_replace( '%taxonomy%', implode( '/', $dir ), $permalink );
return $permalink;
add_filter('post_type_link', 'filter_permalink', 10, 4);

And that will get you there. Please ask if you want me to explain the code.

matthewordie comments:

This got me really close. The page doesn't redirect now, but it throws a 404 error. Do I need to have anything particular in the custom permalink settings in wordpress?

John Cotton comments:

Sorry, I should have added that you need to save your permalinks in the dashboard!

matthewordie comments:

I did resave them, but I'm still getting the 404.

John Cotton comments:

Did you change them to something else, save, change back, save?

Sometimes you need to do that to get WP to flush the rewrite rules.

(You could just stick a flush_rewrite_rules() line in your functions file until you've got it working - but remember to remove once it is, otherwise you'll get a nasty performance hit).

matthewordie comments:

Yeah I flushed and resaved but no go. The permalink shown when editing the wordpress page displays correctly. I still get a 404. I transfered to a different server to double check but still happens.

John Cotton comments:

<blockquote> I still get a 404. I transfered to a different server to double check but still happens.</blockquote>

This can happen if you have another rewrite rule that matches but writes differently AND that rule appears earlier in the list.

Can you try changing 'work/' to - say 'kangaroo/', save permalinks and see what happens?

If you still get a 404 then...well...hmmm...


matthewordie comments:

You want me to actually change the custom post type to something else? Or just type that in the URL?

When I change the URL to something else it redirects to /work.


Luis Abarca answers:

This is a work Custom post type plugin, just copy and paste into a new file in plugins folders and activate.

Plugin Name: Work post type
Description: Plugin for Work custom post type
Author: Luis Abarca
Author Uri:

$product = Work::instance();

class Work {
const CUSTOM_TYPE_NAME = 'work';
const TAXONOMY_NAME = 'work_category';
private static $instance;

* Static functions
public static function instance () {

if ( ! isset( self::$instance ) ) {
$class_name = __CLASS__;
self::$instance = new $class_name;

return self::$instance;


private function __construct()
add_action('init', array($this, 'register_custom_types') );
add_filter('post_type_link', array($this, 'custom_permalinks'), 1, 3);

register_activation_hook(__FILE__, array($this, 'activate') );
register_deactivation_hook(__FILE__, array($this, 'deactivate') );


public function custom_permalinks($url, $post = null, $leavename = false)
// products only
if ($post->post_type != self::CUSTOM_TYPE_NAME) {
return $url;

$post_id = $post->ID;

$taxonomy = self::TAXONOMY_NAME;
$taxonomy_tag = '%' . $taxonomy . '%';

// Check if exists the product type tag
if (strpos($taxonomy_tag, $url) < 0) {
// replace taxonomy tag
$url = str_replace($taxonomy_tag, '', $url);
} else {
// Get the terms
$terms = wp_get_post_terms($post_id, $taxonomy);

if (is_array($terms) && sizeof($terms) > 0) {
$category = $terms[0];
// replace taxonomy tag with the term slug
$url = str_replace($taxonomy_tag, $category->slug, $url);

return $url;

public function register_custom_types()
global $wp_rewrite;
global $wp_query;

register_post_type(self::CUSTOM_TYPE_NAME, array(
'label' => 'Works',
'description' => '',
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'capability_type' => 'post',
'hierarchical' => true,
'rewrite' => array('slug' => self::CUSTOM_TYPE_NAME),
'query_var' => true,
'has_archive' => true,
'menu_position' => 6,
'supports' => array( 'title', 'editor', 'excerpt', 'trackbacks', 'revisions', 'thumbnail', 'author' ),
'labels' => array(
'name' => 'Works',
'singular_name' => 'Work',
'menu_name' => 'Works',
'add_new' => 'Add Work',
'add_new_item' => 'Add New Work',
'edit' => 'Edit',
'edit_item' => 'Edit Work',
'new_item' => 'New Work',
'view' => 'View Work',
'view_item' => 'View Work',
'search_items' => 'Search Works',
'parent' => 'Parent Work')

register_taxonomy(self::TAXONOMY_NAME, self::CUSTOM_TYPE_NAME, array(
'hierarchical' => true,
'label' => 'Work categories',
'show_ui' => true,
'query_var' => true,
'rewrite' => array('slug' => self::CUSTOM_TYPE_NAME . '/' . self::TAXONOMY_NAME),
'singular_label' => 'Work Category')

$wp_rewrite->extra_permastructs[self::CUSTOM_TYPE_NAME][0] = '/' . self::CUSTOM_TYPE_NAME . '/' . self::TAXONOMY_NAME . '/%' . self::TAXONOMY_NAME . '%/%' . self::CUSTOM_TYPE_NAME . '%';


public function activate()

// }}}
// {{{

public function deactivate()


matthewordie comments:

Thanks, I tried your plugin out but I'm getting a Bad Gateway error. And it changes the URL to this:


Luis Abarca comments:

Sorry, i duplicate the taxonomy, try again please

This is a working example.
[[LINK href=""]][[/LINK]]

Luis Abarca comments:

I just change, this

$wp_rewrite->extra_permastructs[self::CUSTOM_TYPE_NAME][0] = '/' . self::CUSTOM_TYPE_NAME . '/' . <strong>self::TAXONOMY_NAME</strong> . '/%' . self::TAXONOMY_NAME . '%/%' . self::CUSTOM_TYPE_NAME . '%';

To this

$wp_rewrite->extra_permastructs[self::CUSTOM_TYPE_NAME][0] = '/' . self::CUSTOM_TYPE_NAME . '/%' . self::TAXONOMY_NAME . '%/%' . self::CUSTOM_TYPE_NAME . '%';


Francisco Javier Carazo Gil answers:

Maybe you are doing it in the right way but not in the right order:

1. define custom post type
2. visit permalinks settings page (which flushes the rewrite rules) -- don't even need to change or re-save the permalink settings
3. go add posts with custom post type

If you flush your rules from the code, remember: Flush rules only on activation or deactivation. Don't do it on any other hook.