Is there any way to add the .html extension to custom post types without plugin ?

For posts I can use /%postname.html on the permalink settings

For pages I can use:

add_action('init', 'change_page_permalink', -1);
function change_page_permalink() {
    global $wp_rewrite;
    if ( strstr($wp_rewrite->get_page_permastruct(), '.html') != '.html' ) 
        $wp_rewrite->page_structure = $wp_rewrite->page_structure . '.html';

For Custom post types ???

Is there any piece of code similar to the one above that allws me to change or to add the .html on the custom post type url ?

  • 5
    Just wondering for what reason you want to add .html?
    – Vince P
    Jul 19, 2012 at 15:29
  • Well, function post-type_permalink will throw an error. If you don't receive this error, then you've got something painfully wrong with your debug settings. Also please rework your question with the correct intends on the lines. Thanks.
    – kaiser
    Jul 25, 2012 at 15:11
  • @kaiser, Is just to know how can I use the same permalink settings on the custom post types. If I set /$postname.html for the posts, how can I use the same for the custom post types.
    – user983248
    Jul 25, 2012 at 15:21
  • You didn't read a single line I wrote in my comment, right? Please fix your code. Also - doesn't work in function names...
    – kaiser
    Jul 25, 2012 at 15:23
  • @kaiser Yes I did, but I'm not Flash, and I have a hand problem, so I can't type as I think or speak
    – user983248
    Jul 25, 2012 at 15:29

3 Answers 3


This seem to work:

Create the rewrite rules like post-type/post-name.html. You can use arrays to create the rules for just some set of post types instead of doing it for all of them.

add_action( 'rewrite_rules_array', 'rewrite_rules' );
function rewrite_rules( $rules ) {
    $new_rules = array();
    foreach ( get_post_types() as $t )
        $new_rules[ $t . '/([^/]+)\.html$' ] = 'index.php?post_type=' . $t . '&name=$matches[1]';
    return $new_rules + $rules;

Format the new permalink structure for these post types.

add_filter( 'post_type_link', 'custom_post_permalink' ); // for cpt post_type_link (rather than post_link)
function custom_post_permalink ( $post_link ) {
    global $post;
    $type = get_post_type( $post->ID );
    return home_url( $type . '/' . $post->post_name . '.html' );

And then stop redirecting the canonical URLs to remove the trailing slash. This might need some more work, as you'll probably want to keep the redirection for most cases.

add_filter( 'redirect_canonical', '__return_false' );

As others said around here, after doing the above you'll need to flush the rules, and that's possible by visiting the options-permalink.php admin page in Dashboard -> Settings -> Permalinks.

  • 6
    Oi Vinicius, a good answering practice is to prefix all function names with wpse_59024_ (Question ID) to avoid conflicts of copy/pasta ;) . . . And has the extra benefit of knowing which Answer does that code belongs in our snippets library. . . . . +1 Spot on!
    – brasofilo
    Jul 28, 2012 at 13:13
  • This code would overwrite any other filters that use the post_type_link filter
    – Tom J Nowell
    Apr 1, 2017 at 15:34

You could add a rewrite rule for this that supersedes the built-in permalinks, e.g. for a custom post type "product"...

add_action('init', 'add_html_ext_to_custom_post_types');
function add_html_ext_to_custom_post_types() {
    add_rewrite_rule('^product/([^/]+)\.html', 'index.php?product=$matches[1]', 'top');

(Don't forget to flush your rules either by re-saving your permalinks or using flush_rules in the way @toscho notes above).


  • I don't think functions like the_permalink() would use this, so you might have to add a filter for post_link to catch those links. You could also add to the redirect_canonical filter to redirect the default permalinks, so that /product/foo and /product/foo/ redirect to /product/foo.html.
  • You would need to add addition rewrites for other URLs that your site used, like feed URLs, subsequent pages, trackbacks, etc. The code above would just work for the main Custom Post Type page.

If you would prefer a WordPress plugin to handle the work for you, check out Custom Post Type Permalinks in the WordPress plugin repository. Tested on WordPress 3.4.1 and it works perfectly.

After activating the plugin, just navigate to Dashboard -> Settings -> Permalinks. You can add specific rewrites for each registered custom post type.

  • 3
    Do not flush the rewrite rules on every page load.
    – fuxia
    Jul 19, 2012 at 14:07
  • that code is not working, also if you just copy and paste code from xplus3.net/2010/05/20/wp3-custom-post-type-permalinks make sure that it works as expected -1 for not testing
    – user983248
    Jul 19, 2012 at 14:11
  • @toscho how do you suppose to do that or to stop that ?
    – user983248
    Jul 19, 2012 at 14:15
  • 1
    flush_rules() should be called on de/activation only because it is very slow. You can check $GLOBALS['wp_rewrite']->rules to see if the rules are known already.
    – fuxia
    Jul 19, 2012 at 14:19
  • @Michael Ecklund It gives a 404 I does not work. Did you test that code ?
    – user983248
    Jul 19, 2012 at 14:20

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