I want my post-links to look like /%post_id62%_%postname%/ where %post_id62% is the post ID in base62 encoding (e.g. http://example.com/y9Rlf_test/) and my shortlinks to consist of /%post_id62% only (e.g. http://example.com/y9Rlf). This is what i've got so far (in my functions.php) which obviously doesn't work:

add_action( 'init', 'yoyo_init' );
function yoyo_init() {
  add_rewrite_tag( '%post_id62%', '([A-Za-z0-9]+)' );

function yoyo_generate_rewrite_rules($yoyo_rewrite) {
  global $wp_rewrite;
  $new_rules['/([0-9A-Za-z]+)/?$'] = 'index.php?p=' .
    base622dec($wp_rewrite->preg_index(1));  // DOESNT WORK OF COURSE (executed only once when rewrite_rules are generated)
  $wp_rewrite->rules = array_merge($new_rules, $wp_rewrite->rules);

add_filter( 'post_link', 'yoyo_post_link', 10, 2 );
function yoyo_post_link( $permalink, $post ) {
  if ( false !== strpos( $permalink, '%post_id62%' ) ) {
    $post_id62 = dec2base62( $post->ID );
    $permalink = str_replace( '%post_id62%', $post_id62, $permalink );
  return $permalink;

add_filter('pre_get_shortlink', 'yoyo_custom_shortlink', 10, 4);
function yoyo_custom_shortlink($false, $post_id, $context, $allow_slugs) {
  global $wp_query;
  if($post_id or ($context == 'query' and $post_id = $wp_query->get_queried_object_id())) {
    return home_url('/' . dec2base62($post_id));
  return false;

function dec2base62($number) {
  $digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';  $string = '';  while($number > 0) { $string = $digits[$number % 62] . $string;  $number = floor($number / 62); }
  return $string;
function base622dec($string) {
  $digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';  $number = 0;  while(strlen($string) and ($number *= 62 or strlen($string))) { $number += strpos($digits, $string[0]);  $string  = substr($string, 1); }
  return $number;

Any idea how to tackle the problem? Bonus points for avoiding the -N slug suffix when there's another post with the same title created (which might be avoided actually when setting the link structure to "/%post_id62%_%postname%/" but in order to have the shortlink functionality it should be "/%post_id62%" I guess; cf. Turn off %postname% auto-incrementing?)! Thanks!

  • 2
    -N suffix is created to make unique slugs. Slugs are meant to be unique and are created from the title, so when you have same title for 2 posts, the same slug is created for 2nd post and then -N is applied to make it unique. If you don't want -N then modify the slug just below the title while creating your new post. Jul 7, 2011 at 9:13
  • @Hameedullah Not true when using something like /%post_id%-%postname%/ according to wordpress.stackexchange.com/questions/7922/…
    – user6254
    Jul 7, 2011 at 17:57
  • 1
    Not true. Did you try creating posts with same name while using %post_id%? It adds -N after the slug, even if you are using %post_id% based permalinks. I just tried this on vanilla WP3.2 and WP3.1.3. Jul 9, 2011 at 9:35
  • 1
    But the second option given in Michael's answer will work for you. Jul 9, 2011 at 9:36
  • Seems I've misunderstood the first option Mike gave. Seems he's wrong there.
    – user6254
    Jul 11, 2011 at 0:25

1 Answer 1


You only made a mistake in the final step, when you convert the base62-encoded ID back to a decimal ID. You should not do this by changing the rewrite rules, as they are only patterns, not actual rules per post.

What you want is take an incoming URL, let WordPress parse it using the rewrite rules, but then before it is passed to the query engine, check for the post_id62 variable and set the actual p (post id) variable based on it. You can do this in the request filter, which is applied in WP::parse_request().

add_action( 'request', 'wpse22253_request' );
function wpse22253_request( $query_vars )
    if ( array_key_exists( 'post_id62', $query_vars ) ) {
        $query_vars['p'] = base622dec( $query_vars['post_id62'] );
    return $query_vars;

However, this will not solve the postname-N problem. WordPress will still call wp_unique_post_slug on the post_name field, and post_name will not include the post_id62 part (since the database is independent of the rewrite rules). As Mike suggested in the followup comments to his answer, you should either figure out a way to include the post_id62 part in the post_name field, or (and this might be the better way) ignore the %postname% part in the URL and just slap a postname-with-stripped-number-suffix string at the end.

You will also run into issues with your shortlinks, since they have the same format (any letter or digit) as a page. Is /banana/ a page with the title "Banana" or a shortlink to the post with ID 34440682410 - which is banana in your base-62 encoding?

  • Thanks a lot! No issues with shortlinks vs pages here; my site doesn’t have pages. No need for author-, category-, ...-pages either, but yes, have to change /feed/ to /_feed/. What annoys me is that the year pattern ([0-9]{4}) jumps in where ((19|20)[0-9]{2}) really should do it. I guess I want to get rid of all those patterns.
    – user6254
    Aug 4, 2011 at 3:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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