0

I have a custom URL like this:

website.com/show/?id=9999&n=page-name 

I'm trying to come up with a rewrite rule to convert it to:

website.com/show/9999/page-name/

Note that this is a WordPress site and /show/ is WP page name.

Here's the rules I'm using in .htaccess:

<IfModule mod_rewrite.c>
    Options -MultiViews
    RewriteEngine On
    RewriteBase /
    RewriteRule ^show/(.*)$ /show/?id=$1 [R=301,NC,QSA]
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
</IfModule>

This is working; it rewrites website.com/show/?id=9999 to website.com/show/9999/.

Then I modified the rule for the second query string:

RewriteRule ^show/(.*)/(.*)$ /show/?id=$1&n=$2 [R=301,NC,QSA]

But now website.com/show/9999/page-name/ returns a 404 error. It works if I go to: website.com/show/9999/?n=page-name.

What am I doing wrong?

Update

The 404 problem is now solved.

However, now I need to redirect the old query string URL:

website.com/show/?id=9999&n=page-name 

to the new SEO friendly URL:

website.com/show/9999/page-name

How do I setup that redirect?

Community
  • 1
  • 1
Bryan
  • 69
  • 8

3 Answers3

1

Try this:

Options +FollowSymLinks

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase /

    RewriteRule ^show/(\d+)/?([^/]*)/?$ /show/?id=$1&n=$2 [L,NC,QSA]

    # Wordpress defaults:
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
</IfModule>
## Results
# show/9999            => show/?id=9999&n=
# show/9999/           => show/?id=9999&n=
# show/9999/page-name  => show/?id=9999&n=page-name
# show/9999/page-name/ => show/?id=9999&n=page-name

As you see, when page-name is not available, there will be an empty n query string parameter. In your PHP script, instead of checking for the parameter presence using isset(), try using empty().

One last note; The above rules do not redirect the request, it maps the request under the hood, so that the URL stays clean. If you need a redirect, just add a R flag.

404

Even if you remove your custom rewrite rules, you'll get a 404 from WordPress. That's because, as you said, the rewrite target (/show) is a WordPress page and ultimately get mapped to the index.php file. Then, WordPress checks its database to see if it can find a page with that path or throws a 404 if it can't. Obviously, the latter is your case.

Update

Regarding your recent update; To redirect the old URL to the new one, you need some rewrite rules along the lines of:

Options +FollowSymLinks

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase /

    RewriteCond %{QUERY_STRING} ^id=(\d+)(&n=)?(.*)$
    RewriteRule ^show/?$ /show/%1/%3 [R=301,QSD,L]

    # Wordpress default rules:
    # ...
</IfModule>
## Results
# show?id=9999              => show/9999
# show?id=9999&n=page-name  => show/9999/page-name
# show/?id=9999&n=page-name => show/9999/page-name

Don't use [R=301] until you're 100% sure that everything's working fine, as it's aggressively get cached by the browser.

Community
  • 1
  • 1
sepehr
  • 17,110
  • 7
  • 81
  • 119
  • This still returns a 404 error for `website.com/show/9999/page-name`. However I can access the page from `website.com/show/?id=9999&n=page-name` – Bryan Jan 04 '17 at 20:33
  • I'm pretty sure that even if you remove your URL rewriting rules, it will still return a 404 error. So, the 404 has nothing to do with the rewrite process. That's because you're relying on a WordPress page for rewrite target. WP checks its database to see if the page exists and throws a 404 because it doesn't. You can put a PHP file there and avoid WP completely if that's possible. – sepehr Jan 04 '17 at 20:33
  • But `/show/9999/` isn't an existing page either, just like `/show/9999/page-name/`. So why does the former work and not the latter? If `/show/9999/page-name` is remapped to `/show/?id=9999&n=page-name` and I can verify that URL is accessible, then why isn't it working? – Bryan Jan 04 '17 at 21:55
  • I tried creating a `show` directory and put `index.php` file in there, but I still get a 404 when visiting `/show/9999/page-name` – Bryan Jan 05 '17 at 00:27
  • I put that rewrite rule in htaccess, but it threw a 500 internal server error. I came up with one that worked, see my answer. Thanks very much for your help! – Bryan Jan 06 '17 at 07:09
  • Well done! You're welcome. BTW, it works without any issues on a vanilla 2.4 Apache installation. – sepehr Jan 06 '17 at 10:44
1

I think I figured it out... almost.

@sepehr is correct that WordPress is checking, not finding the page, and throwing a 404. So I needed to use WordPress' own rewrite engine to define the rewrite rules so it recognizes what I'm doing.

So I added this to my WordPress theme's functions.php file:

add_action( 'init', 'init_custom_rewrite' );

function init_custom_rewrite() {
    add_rewrite_rule( 
        '^show/([^/]*)/([^/]*)/?',        
        'index.php?page_id=2382&id=$matches[1]&n=$matches[2]',        
        'top' 
    );
}

add_filter('query_vars', 'my_query_vars', 10, 1);

function my_query_vars($vars) {
    $vars[] = 'id';
    $vars[] = 'n';
    return $vars;
}

Now the URL website.com/show/9999/page-name works correctly, not throwing a 404.

However, now I need to redirect the old query string URL to this new one. See my updated question.

Update

Here's the rewrite rule to redirect the old query string URLs to the new SEO friendly URLs:

RewriteCond %{QUERY_STRING} ^id=([^/]*)&n=([^/]*)$ 
RewriteRule ^show/?$ /show\/%1\/%2\/? [R=301,L]
Community
  • 1
  • 1
Bryan
  • 69
  • 8
  • Great, well done! I'll update my answer to provide a new rule set to rewrite the old URL to the new one. – sepehr Jan 05 '17 at 09:47
  • Well done. Just a few notes; You don't need those `[^/]` groups when matching against the querystring as it won't include one ever. Also, escaping `/` characters are not needed on the *substitution* argument of the `RewriteRule`. – sepehr Jan 06 '17 at 10:49
0

Please check below rule in to your .htaccess.

RewriteRule ^show/(.+)/(.+)$ show/?id=$1&n=$2 [L,QSA]
Ashish Patel
  • 3,551
  • 1
  • 15
  • 31
  • That still returns a 404 error for `website.com/show/9999/page-name/`. It also breaks my PHP script `$_GET[id]`, so `website.com/show/9999/` doesn't even work. – Bryan Jan 04 '17 at 06:13