Passing Query String Parameters in WordPress URL

WordPress Custom Query String

Update 02/03/2011:
Got a helpful message from JR about custom taxonomies:
I was searching for a solution for a couple days and finally found a way to go to a custom taxonomy page and further refine the results by post_type. This is critical when you have a situation with one taxonomy servicing multiple post-types. I am hoping to save others the aggravation and apparently the comments are closed on this post.

Thanks for the update JR!

It may not be immediately clear to some where a technique like this might come in handy. So I’m using a recent project as an example. This particular project was for a subsidiary of Milacron, one of Cincinnati’s most well-known manufacturing companies. Cimcool develops industrial fluids: additives, rust inhibitors and metalworking fluids are some of their major product lines and those product pages are what we’ll use for this tutorial.


They already had a website but hired us to handle the SEO. Part of that process involved cleaning up the URL structure of the site by removing query strings from the URLs on product pages. The particular page I’m going to cover in this tutorial is this one. If you visit that link and click any of the categories listed on the left-hand side of the page you’ll see that you’re sent to a page that looks identical but has the category name in the URL. In fact, all of those pages are the same “MSDS-PIF” page served by WordPress, while the content shown on each of those pages is determined by a query string that is parsed from the last portion of the URL. This allows us to have keyword-rich URLs for the products categories without having to create separate WordPress pages for each category.


The biggest problem with getting this done in WordPress, as I alluded to in the last paragraph, is that even with pretty permalinks enabled, we would have had to create a new WordPress page with a unique template for each of those product categories. There were only 15 product categories at the time, so that wouldn’t be such a tedious task, until you consider the fact that it makes adding categories a bit of a hassle, in that we’d have to create a new WordPress page and page template anytime the company would need to add categories. While that would mean more billable hours for us, it wouldn’t be in the client’s best interest if it could be avoided.

The Solution

Thankfully, it could be avoided. Instead of creating a bunch of pages and their respective page templates every time new categories are added, we can use WordPress’ ability to specify custom rewrite rules to pass the query string as part of the URL and use a single page and page template to display all product categories.

The Process

The first step was to create a function to add variables to WordPress’ query string and then hook that function into the query_vars hook.

Next, we created a function to add rules to WordPress’ existing array of rewrite rules and hook that function into the rewrite_rules_array hook.

Finally, in the MSDS-PIF page template, we used the $wp_query object’s query_vars property to get the query string from the URL.

The Code

So let’s get down to the code. First we need to create a function to tell WordPress to keep track of our new query variables. This function goes in your theme’s functions.php file.

function add_query_vars($aVars) {
$aVars[] = "msds_pif_cat"; // represents the name of the product category as shown in the URL
return $aVars;

// hook add_query_vars function into query_vars
add_filter('query_vars', 'add_query_vars');

The above process is fairly simple. When the function is hooked into query_vars, WordPress passes the existing array of query variables to the function. We simply add “msds_pif_cat” as another query variable and send the array back to WordPress.

Now WordPress knows, in addition to it’s existing query variables, it should add one called “msds_pif_cat,” which we’ll use to store the product category name as specified in the URL.

However, this alone won’t do the trick. We also need to tell WordPress how to populate that query variable. In Cimcool’s case, we want the product category pages’ URLs to look like this: So, we need to tell WordPress that anytime it finds a URL matching that structure, to use the last portion, the category-name, to populate our new “msds_pif_cat” query variable. To do that we need to add a custom rewrite rule.

We accomplish this with another function in our theme’s functions.php file:

function add_rewrite_rules($aRules) {
$aNewRules = array('msds-pif/([^/]+)/?$' => 'index.php?pagename=msds-pif&msds_pif_cat=$matches[1]');
$aRules = $aNewRules + $aRules;
return $aRules;

// hook add_rewrite_rules function into rewrite_rules_array
add_filter('rewrite_rules_array', 'add_rewrite_rules');

The function is hooked into rewrite_rules_array, so WordPress passes the existing rewrite rules as an array to the function, which we then retrieve as $aRules.

We then add a new rule in the form of an array: array(‘msds-pif/([^/]+)/?$’ => ‘index.php?pagename=msds-pif&msds_pif_cat=$matches[1]‘)

This rule tells WordPress that anytime it finds a URL that includes “msds-pif/”, followed by anything other than a forward slash, followed by another (optional) forward slash, to capture the last portion of that URL (the part within the parenthesis), and serve the page found at “index.php?pagename=msds-pif&msds_pif_cat=$matches[1]“.

By default, WordPress will store the captured portion of the URL in an array called $matches. And so if we have a URL request like “”, WordPress will serve the page at “index.php?pagename=msds-pif&msds_pif_cat=industrial-cleaners”.

This allows us to capture that msds_pif_cat variable on the MSDS-PIF page template and vary the content depending upon the contents of that variable.

Getting the Query Variable

We can’t just use the $_GET superglobal to grab our msds_pif_cat query variable. Instead, we have to access the query_vars property of the $wp_query object.

So, on our MSDS-PIF page template, we’ll access that variable using the following code:

if(isset($wp_query->query_vars['msds_pif_cat'])) {
$sMsdsCat = urldecode($wp_query->query_vars['msds_pif_cat']);

Next steps

In our case, we then pull all products matching the category name as stored in the $sMsdsCat variable from the database and display them on the page. Obviously what you do with that variable is up to you and, from this point on, is no different than if you had used PHP’s $_GET superglobal to get a query variable on a non-WordPress page.

One thing I should mention here is that the rewrite_rules_array hook is called when you update or save your permalink structure, so after you make these changes, you’ll have to re-save permalinks to see the changes take effect.

More examples

Take a look at the Cimcool link above to see all this in action. For an example of using multiple custom query variables, have a look at Screen Innovations’ dealer locator, where we used a similar process to display dealers on the page for a given country, state and city using SEO-friendly URLs and without the need to create a hundred or so page templates.

Finally, check out the following resources for more information on URL rewriting in WordPress:

  1. What is your wp version? I tried in wp 2.9.1 and it doesnt work. I guess the plugin I used in the page template need the query string catched and processed before it renders the page template.

  2. I meant, I can display the variable content passed in query string, but the process of lookup category content seems begun before wp render / call the page template. Do you have any solution to this?

    • Sorry Roy, but I have no idea what you’re asking me here. Can you explain further? ;-)

      • I’m trying to re-phrase my qearlier question:

        I want to append query vars onto urls using the permalinks. My plugins only work currently by using the ?variable=x method and I would really like to have them as

        (where ‘x’ is in a predefined pattern to be recognized)

        Thanks for the assistance toward better plugins for the community.

        Using WP 2.9.1 and 2.9.2

        • That’s a tough one Roy. Without working on it it’s hard to know what the issue could be.

  3. Hi !
    Excellent article, i’m developping a quite smimlar system to generate multiple URL without creating pages and it’s really hard to find some good documentation on this.
    The only weakness in the example given with Screen Dealers is the non support of 404 erros pages in case of non existing variable passed in the URL :
    By the way, it’s shame that the title tag doesn’t change with query variables. Why not hook this element too ?

    • Hi Nico, thanks for the compliment! :)

      As for the Screen Innovations site, we were only contracted to convert the existing code to WordPress. Your suggestions are ideal in my opinion, but they weren’t within the project scope in that case.

  4. Great article, very useful. It’s got me started but how do I add more directory levels? In your example you can drill down from country to state then to city. Cheers.

  5. @jon : Just add a new line to grab the second variable passed in the URL :
    $aNewRules = array(‘
    msds-pif/([^/]+)/?$’ => ‘index.php?pagename=msds-pif&msds_pif_cat=$matches[1]‘),
    msds-pif/([^/]+)/([^/]+)/?$’ => ‘index.php?pagename=msds-pif&msds_pif_cat=$matches[1]&msds_pif_title=$matches[2]‘)
    Don’t forget to add the extra variables added in the ‘query_vars’ filter.

    @John Crenshaw : Do you have an idea on how to handle 404 errors ? I didn’t manage to know which hook to use and when because when I display my results, headers have already been sent.

  6. Not quite there – you may need to add an extra function to make sure wp generates the rewrite rules correctly – see the example at the bottom of I got 404 errors using the example above, but it just needed a couple extra lines. Thanks for the write up!

  7. Remember to flush_rules() when adding rules:

    function flushRules(){
    global $wp_rewrite;

    • Emad, WordPress flushes rewrite rules when you (re-)save permalinks through the wp admin, which is why this tutorial says to make sure and do that.

      Incidentally, if you call the flush_rules function using init filter, it’ll run on every page load, which is overkill and will slow down your app.

      • Yes, if you are going to use flush_rules, only do it in your plug-ins’ activate callback so it is only done once.

        • How to do it if I want to add this in my functions.php theme file?

          • @ryan. I think your only option would be to create a custom option in wp_options upon running a flush function, and then check against that every time the function runs. In short, you should do this from within a function

  8. Hi John, I have a problem which is related that I wondered if you might be able to help with.

    We have a new WordPress site, and want to pass along an affiliate referral parameter. So say someone clicks this link

    we want to preserve that affiliate id somewhere, either passing it along in the querystring from page to page, or stuffing it in some vars collection.

    then on this one page we have a contact us form. when they enter their name, email and message, we want the form to email that partner id along with their message i.e.

    Your Name:: mary
    Email:: [email protected]
    Ship-to Country:: Spain
    Product Name:: test

    Please let me know if you have any questions. I look forward to your quote.

    • You will need to use cookies and/or sessions for this. To enable sessions, you have to put in your wp-config.php file


      Then you can store the your affiliate id in the session superglobal on any page you get the request on by hooking into the ‘wp’ hook (which comes after the query string is parsed and before any output is sent to the user so a good place to do any cookie/session cutting).. then you grab that affiliate id from the session cookies when your form submission is processed and pass it along with the email response.

  9. can this code be used for taxonomy term pages. i would like to use your code to display taxonomy term-specific pages. for example, i have a movie site containing 37k movies and countless number of actors. the way it is setup each actor has its own taxonomy term page. when clicking on an actor, it will list all the movies for that actor. i was able to do that. but i don’t like the look of the wordpress supplied taxonomy page format. i would like to have the term pages list thumbnails instead for each movie for that actor. now, instead of creating thousands and thousands of actor pages, i can have one generic actor page template to display all actors. i hope i am making sense. thanks.

  10. i am actually going thru this tutorial and i am getting error 404. i just want to clarify your instructions:
    1. you said to create a page template. i did that and put the codes:
    if(isset($wp_query->query_vars['celebrityname'])) {
    $scelebs = urldecode($wp_query->query_vars['celebrityname']);
    i also added some codes to process the variable $scelebs.
    2. i also created my own functions file. for some reason, my theme doesn;t allow me to insert new functions to functions.php. so i just require_once the new function file so the page template can reference the functions.
    3. i created a new page titled ‘Celebrities’ using the new template file.
    4. in the browser, to test, i typed in:
    5. martin sheen is a term under taxonomy “actors”.
    what do you think is my problem. is this script possible with taxonomy terms.

    • Looks like it’s working fine to me.

    • Somethings definitely not right there, 404 error. To help with testing, you can get the plugin apache-rewrite-rules and also just go to your blog with /rewrites as the path if you have 2.9.2. Paste the rest of your code here might help.

  11. yeah you’re right. but this doesnt work:

    • Are you doing things the same on both page? It seems odd that one would work and not the other.

    • Your actors page doesnt have Angela Lansbury on it but celebrities/ben-gazarra also 404′s and he is on the actors page. Hard to see what is going on without seeing more of your tag/tax/cat structure, permalink settings, generated rewrite rules, .htaccess and the code in your query_vars and rewrite_rules hooks.

  12. the setup was really a bit complicated. i have a people category which i developed earlier. if you look at that category, the pages for each actor have more details (news, tweets, photos, movies, etc.). later on, i implemented the actor taxonomy where you can have an actor page (showing only movies where that actor appeared in) for every actor mentioned in a movie. all i want to do is reformat the actor taxonomy pages and i created a page named celebrities using the page template celebs according to john’s tutorial.

  13. Hey,

    I tried to implement these features on my site.
    I have changed these 2 lines to my requirements:
    $NewRules = array(‘schedule/(.+)/([^/]+)$’ => ‘index.php?pagename=$matches[1]&spielname=$matches[2]‘);
    and $vars[] = “spielname”;

    And i use this in my page template:
    if(isset($wp_query->query_vars['spielname'])) {
    $spiel = urldecode($wp_query->query_vars['spielname']);

    I have the page schedule and the subpage soccer. With the current $_GET superglobal variable i was able to usw links like this:
    But i wanted to be able to use
    But it now redirects me to
    Do you have any idea why it does not work as it is supposed to?

    Thanks in advance,

  14. Hi there,

    I see folks here are trying to do this with custom taxonomies. This is exactly what I am trying to to in one of my sites. I started a discussion about this subject in the WordPress forums at: but so far I am out of luck in finding some help :-(

    Here is what I am trying to do. In my website I am currently using:

    1. Posts (the default WP post type) for property adverts.
    2. A custom post type called partner for our partners nationwide.
    3. Categories (the default WP) for property types (eg: House, Flat, Condo, etc).
    4. A custom taxonomy type called city for listing our cities (eg: Rome, Milan, Madrid, etc). This taxonomy is used by BOTH posts (i.e.: properties) and partners.

    5. index.php only shows posts of the type post (the default post type used for property adverts).
    6. page-partners.php shows the posts of the type partner (the custom type I created for our partners nationwide).

    So far so good. This is all working fine. However, I noticed a little nuisance with queries being passed using the URL:

    When I browse the categories of my site (eg: /flats/ or /houses/, I can use a query in the URL for my custom taxonomy called city and it works like a charm. But with my custom post type partner, it doesn’t work.

    For example, if I add to my URL: /house/?city=rome
    – WORKS: It will return to me all the posts in the category house that only have the city Rome assigned to them (rome is a term of my custom taxonomy called city).

    However, if I try the above example of using a query in the URL, it doesn’t work.

    For example, if I add to my URL: /partners/?city=rome
    – WON’T WORK: Instead of returning all the posts of type partner that only have the city Rome assigned to them, I still returns me all the partners regardless of their city.

    Any ideas of what I might be doing wrong?

    Thanks in advance,

    • Based on this tutorial, this is what I added on my functions.php theme file:

      // my desperate attempt of passing URL queries of the taxonomy CITY to post type PARTNER inside my page /PARTNERS
      function add_query_vars($aVars) {
      $aVars[] = “city”; // in my case, city is a custom taxonomy
      return $aVars;
      add_filter(‘query_vars’, ‘add_query_vars’); // hook add_query_vars function into query_vars

      // I have a custom page called PARTNERS that is used to display custom posts of type PARTNER
      function add_rewrite_rules($aRules) {
      $aNewRules = array(‘partners/([^/]+)/?$’ => ‘index.php?pagename=partners&city=$matches[1]‘);
      $aRules = $aNewRules + $aRules;
      return $aRules;

      // hook add_rewrite_rules function into rewrite_rules_array
      add_filter(‘rewrite_rules_array’, ‘add_rewrite_rules’);

      However I got no luck with this… What am I doing wrong?

      Any help is greatly appreciated.


      • I noticed you said you found the answer on the WP forums, can you post your answer here?

        • R’phael,

          Yes I found an answer, but so far it only works if I use /index/php?pagename=my-custom-pagetemplate&my-customt-axonomy=my-taxonomy-term

          The reason why I didn’t post the answer in the forums is because I was very tired of working and ding on and on for this solution, and I am getting behind the schedule in my project. So besides the fact I was quite busy, I got some obnoxious replies in from people who I was asking for help. So the pressure of the timeline, the lack of support PLUS the bad attitude I got from some folks has really put me off.

          Don’t take this the wrong way, I’ll be happy to help others, do some brainstorming and share knowledge. I just don’t feel like doing it in forums or sites.

          In fact I plan to release a full in-depth tutorial of what I’ve been doing. It involves a lot of stuff such as custom posts, taxonomies, URL queries, complext query_post() stuff, thmbnails, google maps, custom fields, custom page templates and so on. I think it is enough material for a book to be honest! :-)

          IN THE MEANTIME, feel free to contact me through MSN/LiveMessenger and I’ll be happy to talk to you guys, share my findings exchange ideas. My MSN is: guyrush_yyz [at] yahoo [dot] com

          I’ll be happy to help and to to you guys. But as I said before, away from forums for the moment :-)

  15. In the wordpress forums you said that you found a solution. Would you mind sharing it with us?

  16. Hi Phil,

    Please see my comment above (comment-1356) to R’phael. The short answer is yes I would be happy to share it (no catch). But I’d rather talk privately with individuals, away from forums.

  17. Folks,

    I actually changed my mind and posted my solution back in the forum. You can find it at


    • Hi P,

      Thanks for this! I was wondering how this was going but didn’t have a moment to get in and try to answer it completely. I’m glad you found the answer!

      • John,

        I am happy I could help. If you think there is other ways of doing it, perhaps an improvement, please let us know. I am sure I speak for all of us here :-)

  18. Just wanted to say thanks for posting this – exactly what I was looking for and saved me a lot of time.

  19. This is a great tut and it works great except for one problem, now every page goes to my index page? does anyone else have this problem?

  20. This doesn’t seem to work in WordPress 3.0. I need custom permalink to get tag pages, adding a parameter in the url. It works in wp 2.9.2, but not with the last version

  21. I think thats my problem, so how can we get the same behavior in WP 3?

  22. This seems to be a popular topic, so I’ll take a look at doing this in WP3.0 in the next few days and post an update here. If anyone else comes across something before then, please do post it.

  23. Just went through this tutorial in WP3.0 to see if there were any issues and everything worked just fine. I had no trouble getting query vars to come through on the page.

    If anyone’s having trouble, maybe you could elaborate a bit here and one of us can help.

  24. Thanks for sharing these helpful tips. I have it work fine with 3.0. I’m stuck on one thing though. How do I get it to work with pagination ( Do I have to add more rules and query variables?


  25. Are there any simple plugins that do this stuff?

    I have a simple need that maybe someone here can help me address. I need to take a URL like this:

    and make it behave like this:

    Then on the page I need to be able to use the value $prodnum (=prod123) in a few places within the content of the page. For example, if there were a form there to allow someone to ask a question about the product, it could have a hidden field with $prodnum (or however it’s given) so it shows up as “prod123″.

    Also there could be a query that uses $prodnum to pull up information about that product and display it on the page.

    I don’t want to have a custom template page, just any regular page.

    There are several plugins that help manage taxonomies, but I can’t figure out how to make any of them do just this. I don’t need the taxa added to a database; I don’t need tag clouds; I don’t need fancy ways to build associations. I just need to pick off a value from the URL and make it available as a parameter inside a page.

    Basically, I need to pass out stuff with URLs on it that people will type in, and I don’t want to have the “?prodid=” part. Peole will find a dozen reasons to screw up anything they can, and basically this data is implied anyway.

    Many thanks in advance!

  26. Exactly what i was looking for. Thanks a lot for posting this, John! Cheers.

  27. Hi John, great post, almost exactly what I need, do you know any way to make this work with posts.

    I mean I am sending a var to a post, using your tehnique I ad a rule, like this:
    $aNewRules = array(‘([^/]+)/([^/]+)/?$’ => ‘index.php?pagename=$matches[1]&c=$matches[2]‘);

    If ?pagename is the name of a page it works brilliant, if however it is the name of post, I get redirected to:

    site.tld/post-name/ or whatever permalink structure I set, like site.tld/2010/11/15/post-name/

    and the query value is lost. Maybe you know some way around this.


    • I’m not sure about that Dan, I’ve never had to do this for posts. I believe “pagename” is one of the many parameters available for the query_posts function. You may want to look at that function and see if you can use another parameter, like the post’s ID.

  28. Thanks for showing me this, John. You saved my life. I thought that circumventing WP’s URL structure could only be done by putting the rules in .htaccess myself.

  29. Hi everyone,

    I am working on a project where i have a similar situation. I have successfully managed to create the first and individual pages for each actor.

    I have created a custom post type movies and have a page named page-movies.php which shows the list of all movies.

    Similarly, I have created a taxonomy Actors and have a page actors.php where i have the list of all the actors and a page taxonomy-actors.php which shows the details for the individual actor.

    I access my movies page by and it shows me the list of movies. When i click on a movie name it opens a page with the description of the movie and the list of actors that acted in it. Whenever i click on any actor name the url is changed to and the page shows the actors details and the list of movies that he has acted in.

    Everything fine till here.
    Now i have a page filmography.php which shows only the list of movies that the particular actor has acted in but the problem is i cannot make it to appenf to the url.

    What i would like to do is the filmography.php page be rendered when i type or Currently when i type the above url directly i get the page not found error. Also if anyone can tell me how do i create a link for filmography.php on the actor details page so that when i click on that link i am directed to or with the desired result.

    Can anyone help.

    I am a complete novice in php and i tried whatever is explained above but failed. May be i am missing out on soemthing.

    Hoping for a quick response. ITS URGENT.

    Thanks in advance.

  30. I am having issues with using this and pagination together.

    I have created a rewrite rule based on your code to convert into a url with variables for past, current, future like so

    Problem shows up on the category template i am loading, there are pagination links for each array of exhibition posts, which on load of the resulting /shows/past/page/2 gives 404. If I call up the default wp url for this (/category/exhibitions/page/2) it works. Is there some workaround in defining the new rule which can read the paged variable through the wp default rewrite of that paged variable?

    • I’ve never had to rewrite paginated urls before, so I couldn’t help you on this one. Sorry!

    • Hi I’m having the same problem here with pagination and parameters. Do you already have a solution? Thanks!

  31. - Does this influence the current permalink / pagination behaviour of wordpress?
    - Is it possible to have more than one level with this teqnique? (e.g. and catch all the “variables” level2, level3, level4?

    thanks for your answers.

  32. Good tutorial on an interesting yet not well documented feature of WP.

    Does this method rewrite any URL present in the output (page, post, etc), or only ones internal to a site (permalinks).

    For example, if one edited a post to include a URL would that also be a potential candidate for a rewrite, assuming it also matched the grep string.

    Perhaps another way of posing the question: Does this run on a near-completed page, or as part of the page assembly?


    So, we need to tell WordPress that **anytime it finds a URL matching that structure**, to use the last portion, the category-name, to populate our new “msds_pif_cat” query variable. T

    This rule tells WordPress that **anytime it finds a URL** that includes “msds-pif/”, followed by anything other than a forward slash, followed by another (optional) forward slash, to capture the last portion of that URL (the part within the parenthesis), and serve the page found at “index.php?pagename=msds-pif&msds_pif_cat=$matches[1]“.

  33. To solve the pagination issue, you need to specify extra rules, making sure the pagination rule comes FIRST, like so:

    function add_rewrite_rules($aRules) {
    $aNewRules = array(
    ‘msds-pif/([^/]+)/page/?([0-9]{1,})/?$’ => ‘index.php?pagename=msds-pif&msds_pif_cat=$matches[1]&paged=$matches[2]‘,
    ‘msds-pif/([^/]+)/?$’ => ‘index.php?pagename=msds-pif&msds_pif_cat=$matches[1]‘
    $aRules = $aNewRules + $aRules;
    return $aRules;

    // hook add_rewrite_rules function into rewrite_rules_array
    add_filter(‘rewrite_rules_array’, ‘add_rewrite_rules’);

  34. Thanks! really helped me.

  35. This is an excellent tutorial!

    I used your method to make a single page to display user profiles, replacing your ‘msds_pif_cat’ with a code called ‘person’ for each individual. Now I’d like to extend the functionality on the same WordPress site to make another page which will display group details based on the value of a different querystring variable called ‘group’. I’m trying to figure out how to extend our method to accomodate a second querystring variable, but I’m not sure exactly how to proceed.

    I currently have …

    function add_query_vars($aVars) {
    $aVars[] = “person”;
    return $aVars;

    …can I extend this by simply making…

    $aVars[] = “person, group”;


    And then where I currently have…

    function add_rewrite_rules($aRules) {
    $aNewRules = array(‘person/([^/]+)/?$’ => ‘index.php?pagename=profile&person=$matches[1]‘);
    $aRules = $aNewRules + $aRules;
    return $aRules;

    …extend with (for example)…

    $bNewRules = array(‘group/([^/]+)/?$’ => ‘index.php?pagename=groupProfile&group=$matches[1]‘);
    $aRules = $aNewRules + $bNewRules + $aRules;


    Very grateful if you have any time to look at this.


Razorlight Media
Phone: 513-549-7355
Fax: 866-377-4331
cash, check, credit card, invoice, paypal
2207 Kemper Lane
Cincinnati, OH 45206
700 West Pete Rose Way, #68
Cincinnati, OH 45203