Skip to content

Instantly share code, notes, and snippets.

@rxnlabs
Last active September 9, 2025 14:00
Show Gist options
  • Save rxnlabs/28e278375008fbd12329f26883f0310c to your computer and use it in GitHub Desktop.
Save rxnlabs/28e278375008fbd12329f26883f0310c to your computer and use it in GitHub Desktop.
WordPress - WP: Add /blog to your Post post type only and remove it from your other custom post types and taxonomies after adding /blog

Add a /blog to your WordPress blog posts only and remove it from the other post types

This is useful if you want to know when a user is viewing a blog post in Google Analytics vs a page. Or if you want to just have an easy way to distinguish your blog content from your other content using the URL.

There may be an easier way to do this but all of my Googling didn't turn up any answers that let you modify the post URLs only.

  1. Add the code from remove-blog-front-from-custom-post-types.php to your theme's functions.php or to a custom plugin or some file where it will get loaded. This code prevents the new blog base from being applied to the URLs of Custom POst types and taxonomy archives.
  2. Log into your WordPress admin
  3. Go to Settings -> Permalinks
  4. Select the Custom Structure field and add /blog to the Permalink structure (add this before the /%postname% part of the URL structure)
  5. Save your changes
<?php
/**
* Remove the /blog from URLs that are not Post and are not builtin post types
*
* @param array $args Arguments for the current post type
* @param string $post_name Post type name/slug
*
* @return array Arguments for the custom post type
*/
function update_blog_post_urls_remove_from_cpts( $args, $post_name ) {
// get post types that are NOT builtin AND that are public
$custom_post_types = get_post_types( [ 'public' => true, '_builtin' => false, ], 'names', 'and' );
// remove the /blog from custom post types
// Remove from some other random post types that don't show up in the result of get_post_types() above some reason
$random_other_post_types = [ 'news', 'publications' ];
if ( isset( $custom_post_types[$post_name] ) || in_array( $post_name, $random_other_post_types ) ) {
// preserve the previous rewrite rule definition and only alter the with_front argument
$args['rewrite'] = is_array( $args['rewrite'] ) ? array_merge( $args['rewrite'], [ 'with_front' => false ] ) : [ 'with_front' => false ];
}
return $args;
}
add_filter( 'register_post_type_args', 'update_blog_post_urls_remove_from_cpts', 10, 2 );
/**
* Remove the /blog from the archives of taxonomy terms after adding /blog to WordPress posts
*
* @param array $args Arguments for the current taxonomy
* @param string $taxonomy Taxonomy name/slug
* @param array $object_type Array of object types for the taxonomy (e.g. Post types that this taxonomy is registered to, etc...)
*
* @return array Arguments for the taxonomy
*/
function update_blog_post_urls_remove_from_taxonomies( $args, $taxonomy, $object_type ) {
// preserve the previous rewrite rule definition and only alter the with_front argument
$args['rewrite'] = is_array( $args['rewrite'] ) ? array_merge( $args['rewrite'], [ 'with_front' => false ] ) : [ 'with_front' => false ];
return $args;
}
add_filter( 'register_taxonomy_args', 'update_blog_post_urls_remove_from_taxonomies', 10, 3 );
/**
* Redirect all of the requests for the old blog pasts to the new URLs with the /blog value prepended
*
* @param WP_Query $query Current query object
*/
function redirect_old_blog_urls( $query ) {
global $wp;
// if this request has resulted in a 404 error
if ( isset( $query->query_vars['error'] ) && '404' === $query->query_vars['error'] ) {
$custom_url_struct = get_option( 'permalink_structure' );
$parts = explode( '/', $custom_url_struct );
// Get the custom URL structure that we defined in the Permalinks menu and disregard any of the dynamic %placeholder% values that WP uses
if ( ! empty( $parts ) ) {
foreach ( $parts as $part ) {
if ( false !== strpos( $part, '%' ) ) {
continue;
}
$parts_to_prepend[] = $part;
}
}
if ( ! empty( $parts_to_prepend ) ) {
$new_blog_post_location = sprintf( '%s/%s/%s', home_url(), implode( '', $parts_to_prepend ), $wp->request );
$find_post = url_to_postid( $new_blog_post_location );
if ( ! empty( $find_post ) ) {
wp_redirect( $new_blog_post_location, 301 );
exit;
}
}
}
}
add_action( 'parse_request', 'redirect_old_blog_urls', 10, 1 );
@Saida-Lachgar
Copy link

Saida-Lachgar commented Sep 8, 2025

Multilingual Blog Post URLs with WPML

For multilingual sites, you usually create a Posts Page (like /blog/, /actualités/, /noticias/, etc.) and set it in Settings → Reading as the Blog Page.

Since this page’s slug changes depending on the active language (via WPML in this case), you need to dynamically fetch the correct slug and use it in your post permalinks.

// Step 1: Get the Posts Page Slug Dynamically
function get_current_posts_page_slug() {
    $page_for_posts_id = get_option('page_for_posts');
    if ($page_for_posts_id) {
        return get_post_field('post_name', $page_for_posts_id);
    }
    return 'blog'; // fallback if nothing is set
}

// Step 2: Apply the Slug in Post Permalinks
function custom_post_permalink( $permalink, $post ) {
    if ( $post->post_type === 'post' ) {
        $posts_page_slug = get_current_posts_page_slug();
        $permalink = home_url( '/' . $posts_page_slug . '/' . $post->post_name . '/' );
    }
    return $permalink;
}
add_filter( 'post_link', 'custom_post_permalink', 10, 2 );

// Step 3: Add Rewrite Rules for Translated Slugs
function custom_post_rewrite_rules() {
    $posts_page_slug = get_current_posts_page_slug();

    add_rewrite_rule(
        '^' . $posts_page_slug . '/([^/]+)/?$',
        'index.php?name=$matches[1]',
        'top'
    );
}
add_action( 'init', 'custom_post_rewrite_rules' );

After adding this to your theme’s functions.php (or a small plugin), go to
Settings → Permalinks → click Save.
Make sure your structure is set to:

/%postname%/

Result

Now posts will always include the localized blog page slug:

  • English → /en/blog/post-title/
  • French → /fr/actualites/post-title/
  • Spanish → /es/noticias/post-title/

Meanwhile:

  • Pages stay clean → /en/about/
  • Posts Page itself stays → /en/blog/, /fr/actualites/, /es/noticias/

@rxnlabs
Copy link
Author

rxnlabs commented Sep 9, 2025

@Saida-Lachgar Thanks for adding this code and tip to the gist for supporting multilingual with the WPML plugin. That's useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment