DiggingIntoWordPress

by Chris Coyier & Jeff Starr

New htaccess Code for WordPress Permalinks

Posted by on

While manually upgrading a bunch of old WordPress sites, I realized that the WordPress htaccess rules for permalinks had changed. For many years and versions, the htaccess code that enables WordPress permalinks went unchanged, resulting in an almost sacred set of htaccess directives. Here are the original permalink rules as currently provided at the WordPress Codex:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

When working with WordPress, that snippet is the only htaccess code that is actually required by WordPress, and even then, it’s only necessary if you are using so-called “pretty-permalinks” on your site. Needless to say, there are probably millions of instances of these original permalink rules in place on servers and sites all across the Web. The code is so familiar that many WordPress devs can probably write it from memory. So changing things is actually kind of a big deal.

The NEW WordPress Permalink Rules

There’s really no reason to get all excited about anything. I just tend to get hyped up for anything relating to WordPress or htaccess. So mix them together and I’m all over it. For the new WordPress permalink rules, only a single line was added, and it’s a good one to have in there:

RewriteRule ^index\.php$ - [L]

So that’s the new hotness, and it works great at what it does, which we’ll get to here in a moment. For now, let’s go ahead and add that new directive to our original htaccess ruleset to get the new WordPress permalink rules:

For sites installed in root directory:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

And for sites installed in a subdirectory:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /subdirectory/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /subdirectory/index.php [L]
</IfModule>
# END WordPress

I’m not sure exactly when the htaccess permalink rules were changed, and I’m not sure it really matters, but I’m thinking it happened sometime between versions 3.0.0 and 3.0.4. I didn’t notice it until version 3.0.4, but I am guessing that it happened at 3.0. Somebody let me know that I’m wrong here.

So what does it do?

What is the purpose of the new htaccess directive? Why change something that has worked so well for so long? Better have a good reason to go tampering, right? Yes, and as it turns out, this new rule is involved with canonicalization [1] of your WordPress URLs. I don’t think it was by any means necessary, but it’s definitely an improvement over the original code. The new directive basically strips off any dangling index.php from your WordPress permalinks.

[1] Update: As Robert points out, the new line of code is there to fix an issue with Apache’s mod_rewrite; it has nothing to do with canonicalization, which is handled automatically by WordPress.

For example, with the original permalink rules (and no other htaccess modification), the following URLs all refer to the same page:

  • http://digwp.com/
  • http://digwp.com/index.php
  • http://www.digwp.com/index.php

Fortunately, WordPress already handles the www canonicalization, and so the new line of htaccess takes care of the other end of the URL: the index.php. This may not seem like a big deal, but can have a big impact on script performance, search-engine optimization, and user-experience.

Updating your htaccess rules

So the question now is “do I need to go back and add this new directive to my existing permalink rules?” I think the best answer is that you don’t need to do anything – your WordPress-powered sites will continue to work just fine. But if you want the additional canonicalization that the new rule provides, then you probably should stick it in there.

A couple of things to keep in mind about the new htaccess code:

  • No need to update existing htaccess files unless you want to
  • As with the original code, the new htaccess works in all versions of WordPress
  • If you do add the new line, make sure to remove/adapt any similar/related htaccess rules

For example, as I was going through and updating all of those old sites, I had been using my own recipe for proper permalink canonicalization:

# CANONICAL WP RULES
<IfModule mod_rewrite.c>
 RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.php [NC]
 RewriteRule ^index\.php$ http://example.com/ [R=301,L]
 RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
 RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]
</IfModule>

This code works on any site – not just WordPress installs – to clean up both ends of the URL: the www and the index.php. As I updated my htaccess files with the new WordPress permalink rules, I had to remove this custom canonicalization code to make room (functionally speaking) for the new stuff, which is a much more elegant solution.

29 Responses

  1. Hi
    Using fixed:
    define('NOBLOGREDIRECT', '%siteurl%');
    in wp-config.php the www problem is Be solved

    • Jeff Starr

      Yes that is a cool trick, but even easier is just specifying your www preference in the General Settings in Admin. Super easy!

    • Andrea_R January 11, 2011

      That’s actually a multisite directive.

  2. Gautam Chadha January 11, 2011

    Hi Chris! I just confirmed with a local wordpress install I had on my PC that this line was added in WordPress 3.0.

    And I believe it’s a good addition for all those wordpress users who never tweaked .htaccess file for “index.php” canonicalization.

    • Gautam Chadha January 11, 2011

      Sorry, I was so busy reading the post I didn’t cared to read the author and goofed up with the author name. Thanks Jeff for a wonderful post. :)

  3. This was added in 3.0, as Gautam said, and went relatively unnoticed. It would take an .htaccess aficionado like Jeff Starr to write a detailed post about it. :-)

    Links to the ticket and the changeset:

    http://core.trac.wordpress.org/ticket/11845

    http://core.trac.wordpress.org/changeset/13676

    If I remember correctly, the ticket was originally filed under the Performance component, which is now merged into the Optimization component.

    Cheers!

    • Jeff Starr

      Thanks demetris! Those two tickets are what I should’ve taken the time to read before clicking the Publish button! :)

  4. When I add this rule to my file I get a 404 message when I click on a post.

    RewriteRule ^index\.php$ - [L]

    Things work again when I change it back to

    # BEGIN WordPress
    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
    </IfModule>
    # END WordPress

    Any ideas?

    • Jeff Starr

      If you’re running the current version of WP, something else is probably interfering. I recommend do nothing and not worry about it, unless SEO is super important, in which case I would setup a test install and work forward to pinpoint the issue.

  5. Sorry Jeff, it’s not about canonicalization at all. It’s an optimization tweak for mod_rewrite’s internal idiosyncrasy.

    When mod_rewrite runs the directive …

    RewriteRule . /index.php [L]

    … the “L” option does not tell it to stop, but only to skip the remaining rules on the current pass. It then begins an implicit rewrite from the top of the rule set, which may be somewhere above the WordPress block. When that happens, the -f check is repeated unnecessarily, and the rule processing crashes through the bottom of the WordPress block. With the extra rule, the second pass will end without any rewrite.

    Enjoy,
    Robert Chapin
    (I wrote the patch ;)

    • Jeff Starr

      Thanks for the information, Robert. More checking reveals that I was completely misguided with this post. WordPress has built-in removal of index.php by default, so definitely wasted my time getting caught up in the new code. Ah well, it was fun while it lasted ;)

  6. Hi Jeff,
    This is helpful. I’m currently redoing a website in WordPress which means there will be a number of page redirects. Would these redirects be placed in this same htaccess file? WordPress will be installed in a subdirectory. Thanks! (BTW, Digging Into WordPress is fabulous.)

  7. Thanks Jeff for helpful tips.

  8. This TOTALLY worked. Thanks so much, Jeff!!!

  9. I’m at dead-end.

    Currently developing a wp theme for some hebrew business. It actually needs to support hebrew characters in URL. For now, it just opens up 404 page. Maybe you got any ideas, how to get hebrew URLs working? I might think that it has something to do with .htaccess.

  10. so the difference is on RewriteBase

    can we change them via DashBoard > Settings ?

  11. Jack Godot March 16, 2011

    I am developing a new WPMU v3.1 version of my site. On my old site I had no problem making posts programmatically from a php file in my top directory of wordpress.

    Now when executing the file from a browser it redirects to the “Not Found” page.

    My htaccess file has three extra lines you didn’t mention. Are they creating the problem?

    RewriteRule ^ - [L]
    RewriteRule ^[_0-9a-zA-Z-]+/(wp-(content|admin|includes).*) $1 [L]
    RewriteRule ^[_0-9a-zA-Z-]+/(.*.php)$ $1 [L]

  12. Jack Godot March 16, 2011

    Sorry, I made one mistake in the previous post:
    I was redirected to the home page, but since I have no posts on the site that’s why it returned “Not Found”.

  13. Kim Val March 28, 2011

    When I set up my WordPress site using simple script, I placed it in a folder hereafter name mainblog.com. The root of my store has my main ecommerce site. After install I noted that WordPress does not display properly and the layout and formatting are non-existent.

    The problem I encountered is that whether or not I have permalink structure changed, when I combine the WordPress .htaccess rewrite into my cubecart .htacess file which is at the root, my cubecart main store “main.com” shows the main page, but clicking on any product or category redirects to the WordPress site as determined by the rewrite as shown below

    # BEGIN WordPress
    RewriteEngine On
    RewriteBase /mainblog/
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /mainblog/index.php [L]

    How can I integrate the WordPress .htaccess info into another .htaccess file? Any suggestions?

  14. Hi, Jeff…
    I try add this to my .htaccess:
    ## DISABLE DIRECTORY LISTINGS
    Options -Indexes

    but when i try to browser my site, it’s error… can you help me to figured this out, why it’s happen.

  15. Jeff,

    Great post. I’m running a multi-site blog farm and am having trouble with spammers registering as users of my sub-blogs. Then, when I mark them as spam, the whole blog is marked spam.

    Ideally, anyone who tries to register at a sub-blog should be redirected to the main blog registration page (register.php cause I’m using buddypress).

    Here’s what I’ve got, but it isn’t working. What am I doing wrong?

    RewriteCond %{HTTP_HOST} ^site.com$ [OR]
    RewriteCond %{HTTP_HOST} ^www.site.com$
    RewriteRule ^wp-login.php/?(.*)$ "http://site.com/register/$1" [R=301,L]

    Thanks!

    • Jeff Starr

      Hey Greg, the first thing I would do is remove the quotation marks from the redirect URL in the RewriteRule, so that it looks like this:

      RewriteCond %{HTTP_HOST} ^site.com$ [OR]
      RewriteCond %{HTTP_HOST} ^www.site.com$
      RewriteRule ^wp-login.php/?(.*)$ http://site.com/register/$1 [R=301,L]

      These rules may also need modified/positioned depending on whether the “sub-blogs” are created as subdirectories or subdomains.

      Update: I just noticed the query-string match in the RewriteRule, which isn’t going to work. You’ll have better luck using the QUERY_STRING variable to grab that information.

  16. Just tried it, but it won’t let me log out of the site now.

  17. Clarify – when I added that to htaccess, I am unable to log out of my account.

    It’s possible the redirect part worked though…

    • Jeff Starr

      Yeah that code isn’t really plug-n-play, just meant to help you with some clues to help you on your way. There may be a quick/easy solution online somewhere, but generally it takes time to dial in more complex directives.

  18. hallo and good morning, i found this post very useful.
    so I hope someone could help me.
    I have this problem: want to install wordpress on a reseller domain.
    the problem is that in the htaccess i have to add those line, to make the login page for domains works:
    # This is the code that handles login., cpanel., and webmail.
    RewriteEngine On
    RewriteCond %{HTTP_HOST} ^login\. [NC,OR]
    RewriteCond %{HTTP_HOST} ^cpanel\. [NC,OR]
    RewriteCond %{HTTP_HOST} ^webmail\. [NC]
    RewriteCond %{REQUEST_URI} !^/(cgi-sys|rhost_login)
    RewriteRule ^(.*)$ /cgi-sys/login.cgi [NC,L]

    added these lines the login page works only if I remove the line

    RewriteRule . /index.php [L]

    oherwise does not work.
    but if login works, wordpress does not. i can see only the home page and nothing else.

    someone has any suggestion?
    thank you

Comments are closed. Contact us with any critical information. Thank you!

Code is poetry