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 . "https://wordpress.stackexchange.com/" . $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
.