Using CSS3 PIE with WordPress Plugins and Themes

UPDATE: I have posted an updated method for the latest version of PIE (v2)

CSS3 PIE is a fantastic tool to achieve support for CSS3 styles in versions of Internet Explorer, such as IE& and IE8. If you have been trying to include PIE with your WordPress project you might have had some problems with the way PIE is loaded. Below is an effective solution that works for plugins and themes without having to move “” into the root directory of a site and also without needing to dynamically insert the path to PIE using php in your CSS.

In our css, instead of using


behavior: url(;


we use:


behavior: url(/?pie=true);


If your website is located at, the browser will look for the PIE file here, no matter what page you are on. What we need to do is make sure it retrieves the file based on that url. To do that we are going to add a Query variable for PIE and include a template redirect that checks to see if PIE is being called.


function css_pie ( $vars ) {

$vars[] = ‘pie’;

return $vars;


add_filter( ‘query_vars’ , ‘css_pie’); //WordPress will now interpret the PIE variable in the url

function load_pie() {

if ( get_query_var( ‘pie’ ) == “true” ) {

header( ‘Content-type: text/x-component’ );

wp_redirect( get_bloginfo(‘template_url’).’/inc/’ ); // adjust the url to where is located, in this example we are fetching in the themes includes directory

// Stop WordPress entirely since we just want




add_action( ‘template_redirect’, ‘load_pie’ );


All you have to do is add the above functions in your functions.php or in your plugin file and adjust the path accordingly. If you are running into rendering issues in IE check with the other known issues documented on the CSS3 PIE site.

Updated 8 March 2012:

Changed example to use wp_redirect instead of an include statement.


  1. Weston Ruter says:

    Two concerns with this approach:

    1) It does not allow browsers to cache the file, which is important for performance; I believe IE users would be especially suffer as the would have to be re-downloaded each time and executed, and it’s not a lightweight script. Thus, all .htc files should be served with a future-expires header so that browsers can cache them while browsing. HTML5 Boilerplate has this set to 1 month:

    AddType text/x-component htc
    # HTC files (css3pie)
    ExpiresByType text/x-component "access plus 1 month"'

    2) The PHP should do echo file_get_contents(TEMPLATEPATH . '/inc/'); and not include(...). Doing the latter on anything but PHP scripts is perilous, as it will try to inadvertently execute any PHP code blocks.

    I think the best approach is to use the full (host-less) absolute path from the docroot, like behavior: url(/wp-content/themes/foo/inc/; if you’re wanting the be able to easily reference the file from multiple themes, you could use a symlink, or rather put the script outside of a theme in a shared location, like:

    behavior: url(/wp-content/includes/;

    • admin says:

      Thank you for your comment Weston. The caching is something I’ve been wanting to test. I do think I may have a solution that is friendlier with respect to caching. IE users that are served PIE take a small performance hit by default, but anything that can improve performance is valuable.

      Obviously, if you know the exact location to that’s always going to be better (ideally at for brevity). I’ve seen some themes and plugins that hardcode the url but it assumes for example that wp-content is located straight off the root domain which isn’t necessarily the case on every install. Moving programmatically to a certain folder location doesn’t work in all environments because privileges may differ. And having php statements to insert the url dynamically has a number of downsides as well.
      That’s why this solution is pretty good because it’ll work pretty much in every WP environment without the need for the plugin/theme user to do anything.

      Thank you for pointing out the potential issue with the include statement. I’ll update this post this month with an improved solution.

      • admin says:

        Solution in this post has now been updated and improved by replacing the include statement with a basic redirect. That made a lot more sense to start with. The future=expires header should improve performance even more so, I might add the instructions for that another time.

  2. Rafal says:

    Hey Peter,

    I love your solution and I’ve been using that for couple of projects up to the moment.

    I just found out that the script is not loaded at all when there’s an SSL connection being forced via plugin (in my case it’s being forced by Woo Commerce shop plugin on the checkout page ONLY).

    Is there any known workaround to that?

    Regards, keep up the good work.

  3. AnyDog says:


    I would like to say – Great, this solved it for me. but … I got this error:
    Parse error: syntax error, unexpected T_EXIT in … functions.php on line blahblah
    Any ideas ?
    Thanks …

    • Peter Knight says:

      Probably a typo somewhere, hard to see without seeing your code. Could paste your functions.php in pastebin and I’ll have a quick look.

  4. Bartek says:

    Great solution, and it works !

    I have only one sugestion about:

    behavior: url(/?pie=true);

    What gonna happend when i have wordpress installation in subdirectory (ex. ? I think better idea is delete slash and type only:

    behavior: url("?pie=true");

    Then you can be shure that wordpress take control and serve you right file.



    and sorry for my english 😉

    • Peter Knight says:

      You are right that it won’t work then if it’s in a subdirectory. Overall I think it would be better to append the subdirectory name to the url like my-wordpress/?pie=true, which isn’t hard to do. I’ll test this when I get the chance.

Leave a Reply

Your email address will not be published.