Home Services Contact Info

Stop WordPress Overwriting Custom .htaccess Rules

Posted in: Blogs,WebDev by Richard Hearne on June 11, 2007
Internet Marketing Ireland

For as long As I care to remember I’ve been having issues with my WordPress .htaccess file.

.htaccess file is a small Apache file that lets you do all sorts of funky things with requests made to your server. It’s also one of SEO’s best tools. I have a lot of custom 301 redirects set up, including a redirect which makes my site available only via the www subdomain.

WordPress Permalinks

Well WordPress has a habit of rewriting the .htaccess file to allow some of the SEO-friendly URLs you regularly see (also known as ‘permalinks’). And each time it does so I lose my rules. It’s a royal pain in the arse and when this happened just the other day I thought I’d take the time to fix this for once and for all. I had to dig through the WordPress Codex to see what was causing all the trouble. save_mod_rewrite_rules() is the culprit. That little function, and my own ignorance of how WordPress processes the .htaccess file.

The solution

As with most solutions it’s really very simple. As with most simple solutions it’s only simple if you know about it. So here it is:

WordPress .htaccess file looks like this:

# BEGIN wordpress
<ifmodule mod_rewrite.c>
rewriteEngine On
rewriteBase /
rewriteCond %{REQUEST_FILENAME}!-f
rewriteCond %{REQUEST_FILENAME}!-d
rewriteRule . /index.php [L]
</ifmodule>
# END wordpress

Now here’s the really important bit:

Never place your own rules within the ‘wordpress’ block

The WordPress block is the bit that starts with # BEGIN wordpress and ends with # END wordpress. My mistake was to place my rules within this block (after the rewirteEngine On line). This seemed the sensible thing to do – after all rewrite rules must come after rewirteEngine On, and my understanding was not to repeat this command.

How WordPress rewrites .htaccess files

When WordPress rewrites the .htaccess file it does so by first checking that the file is writeable, then exploding the file using the # BEGIN and # END strings. Anything outside of those markers should be left intact after WP rewrites the file.

In my case I had to add a new block with a second rewirteEngine On so that Apache wouldn’t break (although I don’t think this is strictly the correct way to write the file). Here’s what my new revised .htaccess file looks like:

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

Perhaps the WordPress folk could add an additional comment into the .htaccess file that explains this better?

Well there you have it – how to stop WordPress overwriting your custom .htaccess file rules.

You should subscribe to the RSS Feed here for updates.
Or subscribe to Email Updates now:

29 Comments »

  1. Thanks Richard, I’m off to see if I’ve got anything within the wordpress block.

    Comment by JLH — June 11, 2007 @ 10:11 pm

  2. hiya,
    I do mine the same way, but one question the “” that you have ? Is this just to stop Apache crashing incase great sysadmin forgets to reinstall mod_rewrite ?
    thanks,
    paul

    Comment by paul — June 12, 2007 @ 8:25 am

  3. err in the brackets should be ‘<ifmodule mod_rewrite.c>’ :(

    Comment by paul — June 12, 2007 @ 8:27 am

  4. @John – I’m happy that I’m not the only one to have come acropper with this. So far it seems to be holding out well for me – only reason I’ve needed to update my .htaccess file as been to ad some new referrers to ban.

    @ Paul – yep. Generally you wont need this.

    Rgds to both!

    Comment by Richard Hearne — June 12, 2007 @ 8:37 am

  5. Hey thanks for the advice…I’ve been having trouble with that too…hopefully this will fix the problem!

    Comment by Raymond — August 25, 2007 @ 3:30 am

  6. [...] I stumbled upon this as I was doing a bit of research, and hit upon some excellent posts here and here. [Please do read those posts before going ahead as they are excellent source of information] I had [...]

    Pingback by Stupendous Man » Separate Feedburner feeds and categories feeds using .htaccess — November 21, 2007 @ 4:20 am

  7. Hi Richard. I found this post thanks to a google search in hopes of fixing a wordpress/.htaccess issue I’m having. I wonder if you could shed some light on why my .htaccess file is preventing wordpress pingbacks from working.

    After messing around with my .htaccess I actually got a pingback to work once … once . Something in here is breaking pingbacks.

    Any insight you might have would be greatly appreciated.

    My rewrite rules are:

    # Use PHP5CGI as default
    AddHandler fcgid-script .php
    FCGIWrapper /usr/local/cpanel/cgi-sys/fcgiwrapper .php

    RewriteEngine On
    RewriteBase /
    RewriteRule ^feed/?$ http://feeds.feedburner.com/DavidMeade [QSA,L]
    RewriteRule ^itunes/?$ http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=87086557 [QSA,L]
    RewriteRule ^media/(.+)/?$ /post/post.php?mediaID=$1 [QSA,L]

    # BEGIN WordPress

    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    # END WordPress

    Comment by David Meade — February 20, 2008 @ 4:14 pm

  8. Hi David

    Have to say I wouldn’t know where to start. You’re .htaccess is a little more exotic than most.

    Rgds
    Richard

    Comment by Richard Hearne — February 28, 2008 @ 7:57 pm

  9. Thanks for checking, Richard.

    I just last night got it to work. I’m actually not 100% sure what combination of things I was messing with finally got it to work. :-)

    Comment by David Meade — February 28, 2008 @ 8:33 pm

  10. um, how about you just chmod the .htaccess file to not be writable by the server? as in read only access?

    Comment by kenrick — June 2, 2008 @ 4:29 pm

  11. @Kenrick – simple answer to that is that WP cannot write to .htaccess if you chmod it. WP can actually write some useful stuff in there, and quite a few plugins use this functionality.

    Comment by Richard Hearne — June 3, 2008 @ 8:53 pm

  12. Thank you very much for this.

    It happened a few weeks ago and I assumed it was a server oddity that reset the .htaccess file.

    But today I found that saving any post/page caused the .htaccess to take out all my custom rules.

    I came here and it fixed it. Like you, I thought it made sense to only have 1 if statement rather than 2.

    Many thanks.

    Comment by James T — July 4, 2008 @ 3:57 pm

  13. thanks richard! fixed my problem!

    Comment by markus — July 6, 2008 @ 7:16 am

  14. thanks, it was getting silly this overwriting of my custom .htaccess ;]

    Comment by pkmk — July 23, 2008 @ 9:11 pm

  15. [...] Thanks to Richard at Red Cardinal for pointing out the very simple fix to this problem here. [...]

    Pingback by WordPress: Prevent WP from Overwriting Custom htaccess Rules | WhyPad — September 12, 2008 @ 4:24 am

  16. Ahhhh, thank you for saving me the trouble of trudging through the code.

    Comment by Phil Buckley — January 30, 2009 @ 1:43 am

  17. Brilliant! Solved all my headaches! THANKS!

    Comment by gee — May 25, 2009 @ 7:46 pm

  18. Wow, thank you. This has been making me CRAZY!!!

    Comment by Kevin — June 22, 2009 @ 4:33 pm

  19. Excellent. Thank you so much. And there was me thinking a plugin was at fault!

    Comment by David Robertson — December 4, 2009 @ 11:15 pm

  20. Hey, good tip. I was having the same problem. I just added my custom rules to the bottom. Everything has been fine since.

    Comment by Nick — December 18, 2009 @ 5:05 pm

  21. Great tip. You are correct, a simple comment in the default .htaccess file would have been useful. Thanks!

    Comment by Travis — April 7, 2010 @ 1:29 am

  22. Hello,
    I see that here almost anybody knows something about htaccess file, Can somebody help me with .htaccess file issue?
    I have a wordpress website with some plugins and another subdomains installations, almost everyday or second day I got a 505 internal server error , if I remove .htaccess file it works again until next day or next 2 days.

    I noticed that everytime it happens .htaccess file changes from :

    # BEGIN WordPress

    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    # END WordPress

    to

    # BEGIN WordPress

    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    # END WordPress
    s

    After the problem something adds “s” at the end of the .htaccess file .
    I have no idea what can cause it , could you tell me where can i find the problem ? I have the last wordpress version , my Hosting company is Bluehost.com , i run on:

    Linux
    Architecture x86_64
    MySQL version 5.1.46-log
    Apache version 2.2.15
    Kernel version 2.6.28-10.40.intel.E1000E.BHsmp
    PHP version 5.2.13

    Hope that somebody can help , I am not .htaccess expert at all
    thanks Daniel

    Comment by Daniel — May 22, 2010 @ 12:53 pm

  23. Hi Daniel

    Most likely an installed plugin is adding this. Since it only seems to happen every day or 2 it’s going to be hard to track down the culprit.
    Best advice is to disable plugins in small batches and watch to see if the problem goes away. Once you’ve tracked down a batch that removes the issue then disable each of those one by one to see which is causing the problem.
    If you know of any installed plguins that update .htaccess then try to disable those first to track down culprit.

    Hope you fix the problem, rgds
    Richard

    Comment by Richard Hearne — May 23, 2010 @ 1:52 pm

  24. Thanks a lot Richard !! I will do it exactly like you ve said and will let you know , is there some other way to see if some of the plugins are updating my .htaccess ? search in plugin script code etc? i will disable them everytime i will see that the 505 server continues..
    btw. I have made some investigations and have found that the URL from my error logs belongs to one very big forum in china dedicating a lot to the spamming and is hosted on the same hosting(bluehost)
    hxxp://makemoney.vipearn.com/ have changed http to hxxp to not spam here.
    It lets me think that if they share the same server they could also hack bluehost – not me.

    Comment by Daniel — May 23, 2010 @ 3:33 pm

  25. Thanks! I appreciate your post -it’s great to find a solution to a problem immediately after it happens. :)

    Comment by Lanny — December 10, 2010 @ 5:41 pm

  26. Hi Richard – i have a problem with W3 Total Cache plugin – whenever i try to update Core WordPress version on my sites these rules always get overwritten – i’ve tried enclosing the W3 Total Cache rules in these tags:

    RewriteEngine On
    [... ]Funky custom rules go here[ ...]

    However, whenever i go back into my wp-admin area to the w3 total cache settings im asked to reapply my cache settings and that reverts my htaccess file back the way it was – the only difference being that I have empty code at the top of the file – here’s what the file looks like now:

    RewriteEngine On

    # BEGIN W3TC Browser Cache

    AddType text/html .html .htm
    AddType text/richtext .rtf .rtx
    AddType image/svg+xml .svg .svgz
    AddType text/plain .txt
    AddType text/xsd .xsd
    AddType text/xsl .xsl
    AddType text/xml .xml

    ExpiresActive On
    ExpiresByType text/html A3600
    ExpiresByType text/richtext A3600
    ExpiresByType image/svg+xml A3600
    ExpiresByType text/plain A3600
    ExpiresByType text/xsd A3600
    ExpiresByType text/xsl A3600
    ExpiresByType text/xml A3600

    BrowserMatch ^Mozilla/4 gzip-only-text/html
    BrowserMatch ^Mozilla/4\.0[678] no-gzip
    BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
    BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html

    Header append Vary User-Agent env=!dont-vary

    AddOutputFilterByType DEFLATE text/css application/x-javascript text/html text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon

    FileETag None

    Header set X-Powered-By “W3 Total Cache/0.9.1.3″

    FileETag None

    Header set X-Powered-By “W3 Total Cache/0.9.1.3″

    FileETag None

    Header set X-Powered-By “W3 Total Cache/0.9.1.3″

    # END W3TC Browser Cache

    # BEGIN W3TC Page Cache

    RewriteEngine On
    RewriteBase /
    RewriteCond %{HTTP_USER_AGENT} (2\.0\ mmp|240×320|alcatel|amoi|asus|au\-mic|audiovox|avantgo|benq|bird|blackberry|blazer|cdm|cellphone|danger|ddipocket|docomo|dopod|elaine/3\.0|ericsson|eudoraweb|fly|haier|hiptop|hp\.ipaq|htc|huawei|i\-mobile|iemobile|j\-phone|kddi|konka|kwc|kyocera/wx310k|lenovo|lg|lg/u990|lge\ vx|midp|midp\-2\.0|mmef20|mmp|mobilephone|mot\-v|motorola|netfront|newgen|newt|nintendo\ ds|nintendo\ wii|nitro|nokia|novarra|o2|openweb|opera\ mobi|opera\.mobi|palm|panasonic|pantech|pdxgw|pg|philips|phone|playstation\ portable|portalmmm|ppc|proxinet|psp|pt|qtek|sagem|samsung|sanyo|sch|sec|sendo|sgh|sharp|sharp\-tq\-gx10|small|smartphone|softbank|sonyericsson|sph|symbian|symbian\ os|symbianos|toshiba|treo|ts21i\-10|up\.browser|up\.link|uts|vertu|vodafone|wap|willcome|windows\ ce|windows\.ce|winwap|xda|zte) [NC]
    RewriteRule .* – [E=W3TC_UA:_low]
    RewriteCond %{HTTP_USER_AGENT} (acer\ s100|android|archos5|blackberry9500|blackberry9530|blackberry9550|cupcake|docomo\ ht\-03a|dream|htc\ hero|htc\ magic|htc_dream|htc_magic|incognito|ipad|iphone|ipod|lg\-gw620|liquid\ build|maemo|mot\-mb200|mot\-mb300|nexus\ one|opera\ mini|samsung\-s8000|series60.*webkit|series60/5\.0|sonyericssone10|sonyericssonu20|sonyericssonx10|t\-mobile\ mytouch\ 3g|t\-mobile\ opal|tattoo|webmate|webos) [NC]
    RewriteRule .* – [E=W3TC_UA:_high]
    RewriteCond %{HTTPS} =on
    RewriteRule .* – [E=W3TC_SSL:_ssl]
    RewriteCond %{SERVER_PORT} =443
    RewriteRule .* – [E=W3TC_SSL:_ssl]
    RewriteCond %{HTTP:Accept-Encoding} gzip
    RewriteRule .* – [E=W3TC_ENC:.gzip]
    RewriteCond %{REQUEST_METHOD} !=POST
    RewriteCond %{QUERY_STRING} =””
    RewriteCond %{REQUEST_URI} \/$
    RewriteCond %{REQUEST_URI} !(\/wp-admin\/|\/xmlrpc.php|\/wp-(app|cron|login|register|mail)\.php|wp-.*\.php) [NC,OR]
    RewriteCond %{REQUEST_URI} (wp-comments-popup\.php|wp-links-opml\.php|wp-locations\.php) [NC]
    RewriteCond %{HTTP_COOKIE} !(comment_author|wp-postpass|wordpress_\[a-f0-9\]\+|wordpress_logged_in) [NC]
    RewriteCond “/home/w30oil/public_html/wp-content/w3tc/pgcache/$1/_index%{ENV:W3TC_UA}%{ENV:W3TC_SSL}.html%{ENV:W3TC_ENC}” -f
    RewriteRule (.*) “/wp-content/w3tc/pgcache/$1/_index%{ENV:W3TC_UA}%{ENV:W3TC_SSL}.html%{ENV:W3TC_ENC}” [L]

    # END W3TC Page Cache

    # BEGIN WordPress

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

    # END WordPress

    Appreciate any help you can give me to figure this out :-)

    Comment by John — March 31, 2011 @ 12:32 pm

  27. mmm, looks like all the opening & closing ifmodule tags have been removed from my previous post – not sure why that happened – anyway, i hope you get what i meant by all that.

    Comment by John — March 31, 2011 @ 12:34 pm

  28. Hi Richard,

    I use a bunch of custom rewrite rules (I also use w3 total cache), everything worked fine uptill an update to wp 3.4 (or so).

    After trying for days now, slightly desperate I applied your solution and now everything works again!

    Funny since I am writing this in july 2012 and your post is from 2007 – worked for me!

    Thanks a lot for sharing!

    Kind regards,

    Tobias Beuving

    Comment by Tobias Beuving — July 22, 2012 @ 4:22 pm

  29. Thanks for this post, I had the same issue and was trying to figure out how to make wordpress don’t override my rules.

    Comment by Leandro — December 1, 2012 @ 8:31 am

Comments Feed TrackBack

Leave a comment