Half-Elf on Tech

Thoughts From a Professional Lesbian

Category: How To

  • Cacheless (or not)

    Cacheless (or not)

    ETA: As of a month later, I’ve actually switched from APC to Zend Optimizer+

    FilesDon’t get me wrong, I love caching. I love W3 Total Cache (I’m willing to spend my ‘free time’ testing it, after all), and WP Super Cache saved my life once. So why, on a day where I got a 400-600% uptick in traffic (not a joke), did I turn all my caching off? I’m daring, and a little crazy, but I wanted to see if it could be done. I would not have tried this if I was on a smaller server: if you’re getting as much traffic as I am, and you’re on shared hosting, you really need to move to a VPS or Dedicated Server if you want to turn off caching via plugins. It’s not to say that caching is better or worse than not-caching, or vice versa, or that one is a rich-man/poor-man equivalent of the other. Caching plugins are an inexpensive way to speed up your site, and if you can’t afford a bigger server they will buy you the time you need to figure out a better solution. Even with a good plugin and setup, if you get hammered with a lot of traffic, you will crash your site unless the server’s optimized too. Again, what I’ve done is not something I’d try on a low-end server with high traffic.

    When I started measuring the effectiveness of all this, I used:

    To understand what caching is and why we use it, it’s good to understand the basic concepts, and to start by looking at what caching plugins are, how they work, and where their pitfalls are.

    • There are parts of your website that don’t change often (images, javascript, CSS, etc).
    • You want the user to only download what’s changed.

    That sounds easy, but WordPress isn’t static HTML, it’s PHP, and that means every time you visit the page, it runs various proceses to give you the latest and greatest files. The problem with this dynamic code is where content changes rapidly (think ‘comments’ or ‘forums’ or ‘BuddyPress Groups’). Suddenly caching ‘pages’ as wholesale chunks of html doesn’t help if you have to re-cache when someone leaves a remark. Add in the possibility of 4 or 5 people commenting at once, for 12 hours, and now you’re risking a thrashing situation where you keep trying to cache, but it keeps flushing. This is why most people use plugins that handle things elegantly, or try to, where the ‘static’ part of the page (sidebar, etc) are HTMLized, but the dynamic part is left alone. This helps when you have a portion of every page is dynamic, like a shopping cart with a ‘Your order…’ box.

    StorageBut the downside is that you have to write fancy code that remains dynamic portions, and while it certainly can be done, it’s not fun, and let’s be honest, a theme developer doesn’t know which cache you’re going to use, so how can it write the right way for that? The only way to make a truly dynamic and cachable site is to do it from day one, with your theme, server, and plugins all crafted to provide the best experience. And then we have reality, which is we start with something simple, wake up to something large, and experience growing pains.

    Accepting the fact that we’re not starting from nothing, that we have an existing site with content and activity, the first thing most people do is install a plugin. Now, back to what I said before, this isn’t a bad thing. It’s a good first step and will buy you time. It’ll also show you where you need to go. If you don’t have server root access, this may be a your limit, too, as some of the other things I like to do to speed things up without a cache will require it (or you’ll have to ask your hosts and they may tell you to upgrade).

    If you’re going to use a plugin, WP Super Cache (WPSC) and W3 Total Cache (W3TC) are the best two. W3TC is way more advanced, and has a lot of extra bells and whistles, but personally I find that once you can master it, you’re well on your way. Remember though, you’re sacrificing a lot of control here by using a plugin. They’re going to, by their nature, cache everything they can, and we’re back to where we were with the dynamic site generation issue. W3TC has a bunch of extra .htaccess/nginx rules which parse data before you hit WordPress. WPSC can do that, or use PHP (which is slower).

    The dynamic nature of my site is what drove me away from caching plugins. I use other CMS tools, and for my infrequently updated Wiki and ZenPhoto Gallery, where content is very much static, caching makes perfect sense. But when I want to run a simple community site with WordPress, I have to consider all aspects of user experience. Speed is hugely important, but so is the user getting the content they want. Stale content is a killer.

    The reason I decided to see if my site ran slower without caching was that I was reinstalling caching and I thought “This is a perfect time to benchmark.” When I did I was astounded. There was very little difference in a benchmark test. Really no difference between at all, since it was within the results of each other, but I neglected to save the results at the time. I did however snap a picture of my server load(The unrelated part is where I was uploading 10megs of media. Unrelated.):

    load-graph

    Browser Caching is the first thing to tweak, as that tells browsers to cache content. The way this works is your .htaccess tacks on extra information while content like images and CSS are being downloaded, to say “This content is good for X days.” With WordPress, you don’t have to worry about changing the CSS, as most themes and plugins are extra smart, in that they append a version to the end of your CSS like this: style.css?ver=1.9.1 That 1.9.1 is the version of Genesis I’m running, so when that changes, the version changes, and browsers see it as a new file and re-download. That’s pretty cool. (I do wish that child themes pulled in their version, so you could increment that way.) We still have to tell the broswers to cache, and for how long, so near the top of my .htaccess (just below my hotlink protection) I have this:

    ## BEGIN EXPIRES ##
    
        ExpiresActive On
        ExpiresByType image/jpg "access 1 year"
        ExpiresByType image/jpeg "access 1 year"
        ExpiresByType image/gif "access 1 year"
        ExpiresByType image/png "access 1 year"
        ExpiresByType image/x-icon "access 1 year"
    
        ExpiresByType text/css "access 1 month"
        ExpiresByType text/html "access 1 hour"
    
        ExpiresByType application/pdf "access 1 month"
        ExpiresByType application/x-javascript "access 1 month"
        ExpiresByType application/javascript "access 1 month"
        ExpiresByType text/javascript "access 1 month"
        ExpiresByType text/x-js "access 1 month"
    
        ExpiresByType application/x-shockwave-flash "access 1 month"
        
        ExpiresByType video/quicktime "access 1 month"
        ExpiresByType audio/mpeg "access 1 month"
        ExpiresByType video/mp4 "access 1 month"
        ExpiresByType video/mpeg "access 1 month"
        ExpiresByType audio/ogg  "access 1 month"
        ExpiresByType video/ogg  "access 1 month"
    
        ExpiresDefault "access 2 days"
    
    ## END EXPIRES ##
    

    I’ve added in only the types used by my site. I used to use Pragma caching headers as well, but I noticed that Google PageSpeed Insights and YSlow ignore them. Turns out that Pragma headers aren’t honored all the time, in fact, they aren’t honored often, so I just removed them. I don’t think it slowed my site down to have them, but the less to maintain, the better. This had an immediate positive impact, so it was time to look at the server.

    ShapesOver the years, I’ve tuned httpd.conf so it doesn’t crash, I’ve got CSF locked down to prevent people from DoS’ing me over TimThumb, and I of course have APC turned on. Recently I broke down and installed mod_pagespeed when I upgraded to PHP 5.4. Just those things have done a lot to make my site run faster. I intentionally skipped things like Varnish or TrafficServer, as well as a CND or Google’s PageSpeed Service. I (still) don’t need them.

    Since I’m new to Page Speed, I decided to look deep into the filters and enabled the following for the whole server: rewrite_javascript, rewrite_css, collapse_whitespace, elide_attributes. This had a right-away impact of what I jokingly called ‘Effective Minification.’ These filters are new to me, so I spent a lot of time reading up on all the filters, and I find them highly interesting. By having PageSpeed handle things like offloading jQuery, I take the load off of WordPress and other CMS tools, and don’t have to use a plugin.(Don’t get the wrong idea. There are uses for plugins! But I’m all about using the right tool for the right job. I don’t have plugins handle my WordPress database, because I feel it’s like using a screwdriver to hammer in a nail. You can….)

    I added in a couple more to my standard: remove_comments and rewrite_images. Then I went back to my site’s .htaccess and started turning on the things I wanted per-site.

    The ones I picked are:

    Putting those in my .htaccess looks like this (note: no spaces between the filter names, or it all blows an error 500):

    
        ModPagespeedEnableFilters move_css_to_head,defer_javascript,insert_ga
        ModPagespeedAnalyticsID UA-MYCOOLID-1
    
    

    That also means I don’t have to use a plugin to use Google Analytics for my whole site! This may not mean a lot to you, but I have multiple ‘apps’ on my site (four now) and when I edit themes, if I don’t have to do anything, it’s easier. Google will tell you not to do this, but unless they have a way for me to set pagespeed.conf in the /home/user/ folder of my server, I don’t know another per-user way about this.

    Finally I went back on my word, and I installed a plugin. APC by Mark Jaquith. This isn’t a full reversal on my ‘No Plugins!’ stance before, though. All APC is, you see, is but one file that sites in wp-content and kicks things over to APC. Doing this alone moved my TTFB from an F to a B. Which is pretty impressive. Giving it a little time to bake, this worked out okay.

  • wp-cron it up

    wp-cron it up

    CronIf you’ve spent much time mastering *nix servers, you’ve run into cron. I love cron. Cron is a special command that lets you schedule jobs. I use it all the time for various commands I want to run regularly, like the hourly check for new posts in RSS on TinyRSS or RSS2Email, or the nightly backups, or any other thing I want to happen repeatedly. Cron is perfect for this, it runs in the background and it’s always running.

    My first thought, when I heard about wp-cron, was that it clearly tapped into cron! If I scheduled a post, it must write a one-time cron job for that. I was wrong. Among other reasons, not everyone has cron on their servers (Windows, I’m looking at you), but more to the point, cron is a bit overkill here. Instead WordPress has a file called wp-cron.php which it calls under special circumstances. Every time you load a page, WordPress checks if there’s anything for WP-Cron to run and, if so, calls wp-cron.php.

    Some hosts get a little snippy about that, claiming it overuses resources and ask that you disable cron. One might argue that checking on every pageload is overkill, but realistically, with all the other checks WordPress puts in for comments and changes, that seems a bit off. In the old WordPress days, this actually was a little true. If you have a server getting a lot of traffic, it was possible to accidentally loop multiple calls at the same time. This was fixed quite a bit in WordPress 3.3 with better locking (so things only run once). Multisite was a hassle for a lot of people, but seems to have been sorted out.

    StopwatchWe already know that WordPress only calls wp-cron if it needs to, so if you disable it, you have to run that manually (which ironically you could do via a cron job (Read How to Disable WordPress WP-Cron for directions on how to disable and then use real cron.)). To disable wp-cron, just toss this in your wp-config.php:

    define('DISABLE_WP_CRON', true);

    Now, remember, you still want to run a ‘cron’ job to fire off anything scheduled, so you have to add in your own cron-job for this, which I actually do! I have cron turned off, and a ‘real’ cron job runs at 5 and 35 past the hour to call wp-cron with a simple command:

    curl http://example.com/wp-cron.php
    

    Now since I have multiple domains on this install, I actually have that for every site on my Multisite. After all, wp-cron.php on Ipstenu.org doesn’t run for photos.ipstenu.org and so on. Doing this sped up my site, and put the strain back where I felt it should be. The server.

    By the way, wp-cron does more than schedule posts. That’s what checks for plugin, theme and core updates, and you can write code to hook into it to schedule things like backups. Remember before how I said that WP checks to see if there’s anything scheduled? This is a good thing, since if you have your server run a long job (like backing up a 100meg site), you don’t want to wait for that to be done before you page loaded, right?

    For most people, on most sites, this is all fine to leave alone and let it run as is. For the rest, if you chose to use cron instead, keep in mind how things are scheduled. Like I set my cron to run and 5 and 35, so I tend to schedule posts for 30 and the hour. That means my posts will go up no later that 5 minutes after. That’s good, but if I use my DreamObjects plugin, the ‘Backup ASAP’ button schedules a backup to run in 60 seconds (so as not to stop you from doing anything else, eh?), which means I’d have to manually go to example.com/wp-cron.php in order to kick it off. Clearly there are issues doing this for a client.

  • Backwards Settings API

    Backwards Settings API

    backwardsThe biggest problem with documentation is that if you don’t think the same way the doc was written, the examples will frustrate you to the point where you want to cry. If you’re not techy at all, directions to move WordPress to a subfolder will piss you off. If you’re a basic webmaster, who can edit posts and maybe FTP, they’re scary but doable. It’s all a measure of leveling up and seeing things in a way you understand.

    Recently I was banging around with the Settings API in WordPress to fix some debug errors in a plugin. It took me about 5 hours to figure out what it was I was doing, and how to fix it. Actual fixing? About half an hour, including the time it took to make fresh coffee.

    What was my problem? I was looking at code from a different angle. If you start ‘right’ it’s really easy to follow tutorials, but when you start from an already functioning plugin and want to correct it, it’s a nightmare. Reverse engineering things isn’t easy. You’re used to looking at things in one way, and changing that is a headache. What I had was working, up until you turned on debug, which is why I got away with it for so long, and why I hated having to change it.

    But I did. I had a plugin admin page that let you enter two text strings, an access key and a secret key, which the rest of the code used to do it’s magic. Because of that, I couldn’t just be lazy and use a basic register_setting() call like this:

    register_setting( 'myplugin-retain-settings', 'myplugin-retain');
    

    That’s easy, you put it in, you call it with settings_fields('myplugin-retain-settings');, and you go to the races. But I have two strings, and if you call the settings fields twice, you’ll find all sorts of fun errors.

    To explain, let me show you what I did wrong, and what right is.

    Doing It Wrong

    <form method="post" action="options.php">
    <input type="hidden" name="action" value="update" />
    <?php wp_nonce_field('update-options'); ?>
    <input type="hidden" name="page_options" value="myplugin-accesskey,myplugin-secretkey" />
    
    <table class="form-table">
        <tbody>
            <tr valign="top"><th colspan="2"><h3><?php _e('MyPlugin Settings', myplugin); ?></h3></th></tr>
            <tr valign="top">
                <th scope="row"><label for="myplugin-accesskey"><?php _e('Access Key', myplugin); ?></label></th>
                <td><input type="text" name="myplugin-accesskey" value="<?php echo get_option('myplugin-accesskey'); ?>" class="regular-text"/></td>
            </tr>
    
            <tr valign="top">
                <th scope="row"><label for="myplugin-secretkey"><?php _e('Secret Key', myplugin); ?></label></th>
                <td><input type="text" name="myplugin-secretkey" value="<?php echo get_option('myplugin-secretkey'); ?>" class="regular-text"/></td>
            </tr>
    </tbody>
    </table>
    
    <p class="submit"><input class='button-primary' type='Submit' name='update' value='<?php _e("Update Options", dreamobjects); ?>' id='submitbutton' /></p>
    </form>
    

    Doing It Right

    <form method="post" action="options.php">
    	<?php
            settings_fields( 'myplugin-keypair-settings' );
            do_settings_sections( 'myplugin-keypair_page' );
            submit_button();
    	?>
    </form>
    

    Making Wrong Right

    As you can tell, there’s a huge difference between the two, and one is a lot easier to look at than the other.

    First you have to set up registering settings. Since I already had an admin action for my settings page, I wanted to just add to that:

    function add_settings_page() {
            load_plugin_textdomain(myplugin, MYPLUG::getPath() . 'i18n', 'i18n');
            add_menu_page(__('MyPlugin Settings'), __('MyPlugin'), 'manage_options', 'myplugin-menu', array('MYPLUG', 'settings_page'), plugins_url('myplugin/images/myplugin-color.png'));
    }
    

    I added this in(I’m using an array to isolate my plugin functions, so I don’t have to worry as much about namespace clases.):

            add_action('admin_init', array('MYPLUG', 'add_register_settings'));
    

    That was the easy part. The hard part is converting all my table information into settings. Making another new function, I want to add a setting section, just as you would for a one-off. But in this case I need to make a group. Since I named my settings field myplugin-keypair-settings, and my section myplugin-keypair_page, that’s the first important information I need. But backwards.

    First I have to add my section using add_settings_section():

    add_settings_section( 'myplugin-keypair_id', 'MyPlugin Settings', 'myplugkeypair_callback', 'myplugin-keypair_page' );
    

    Once you do that, you want to register the setting, just like normal:

    register_setting( 'myplugin-keypair-settings','myplugin-key');
    

    The complete function

    function add_register_settings() {
         // Keypair settings
            add_settings_section( 'myplugin-keypair_id', 'MyPlugin Settings', 'myplugkeypair_callback', 'myplugin-keypair_page' );
            
            register_setting( 'myplugin-keypair-settings','myplugin-key');
            add_settings_field( 'myplugin-key_id', 'Access Key', 'myplugkey_callback', 'myplugin-keypair_page', 'myplugin-keypair_id' );
            
            register_setting( 'myplugin-keypair-settings','myplugin-secretkey');
            add_settings_field( 'myplugin-secretkey_id', 'Secret Key', 'myplugsecretkey_callback', 'myplugin-keypair_page', 'myplugin-keypair_id' );
    
            function myplugkeypair_callback() { 
                echo '<p>'. _e("Once you've configured your keypair here, you'll be able to use the features of this plugin.", MyPlugins).'</p>';
            }
        	function myplugkey_callback() {
            	echo '<input type="text" name="myplugin-key" value="'. get_option('myplugin-key') .'" class="regular-text"/>';
        	}
        	function myplugsecretkey_callback() {
            	echo '<input type="text" name="myplugin-secretkey" value="'. get_option('myplugin-secretkey') .'" class="regular-text"/>';
        	}
    
    }
    

    imagesWhen I got around to the massive amounts of design I put into things, I ended up making a file called lib/settings.php where I stored everything related to the settings. For me, that’s easier to manage than one massive file with lots of different calls. Easier to debug and edit too, without panicing that I broke something.

    I don’t know if it was so much I think backwards, or the API was backwards, but whatever it was, I had a massive brain-block about all this for about a day. I really have to thank Kailey and Pippin for pointing me at examples that ‘clicked’ in a way that I suddenly could figure it all out.

  • Kick PageSpeed Up A Notch

    If you’re using Apache and PHP 5.3 on your DreamHost domain, you have the magical power to enable Google PageSpeed. Just go and edit your domain and make sure you check the box for “Page Speed Optimization”:

    PageSpeed Option

    But what does that even mean, I hear you ask?

    partnersPageSpeed is Google’s way to speed up the web (yeah, that was redundant), and it serves as a way for your server to do the work of caching and compressing, taking the load off your webapps. Like WordPress. Anyone can install this on their apache server, and it’s free from Google Developers: PageSpeed Mod. Since you’re on DreamHost, you lucky ducky you, we did it for you. Now you can sit back and relax.

    The first thing to notice when you turn on PageSpeed is that it minifies your webpage. That means it takes your pretty formatted source code and gets rid of the extra spaces you don’t use. This is called by using the PageSpeed filter “collapse_whitespace.” Another filter we use is “insert_ga” which is how we’re magically able to insert your Google Analytics for you from your panel. That filter automatically inserts your GA code on every page on your domain. That’s right! No more plugins!

    If you’re like me, you may start to wonder what other filters you should use, and that entirely depend on what you want to remove. I knew I wanted to remove code comments like the following:

    <!-- #site-navigation -->
    

    That’s easy! There’s a filter for “remove_comments” so I can just use that. They have a whole mess of filters listed in the Filter Documentation and reading through it took a while. If you read each one, at the bottom they talk about how risky a certain filter is. Taking that into account, I went ahead and added some low and some high risk filters, since I know what I’m using.

    The magic sauce to add all this is just to edit your .htaccess and put in the following near the top:

    <IfModule pagespeed_module>
        ModPagespeed on
        ModPagespeedEnableFilters remove_comments,rewrite_javascript,rewrite_css,rewrite_images
        ModPagespeedEnableFilters elide_attributes,defer_javascript,move_css_to_head
        ModPagespeedJpegRecompressionQuality -1
    </IfModule>
    

    Really, that’s it.

    The ones I picked are:

    • remove_comments – Remove HTML comments (low risk)
    • rewrite_javascript – minifies JS (med. to high risk, depending on your site)
    • rewrite_css – parses linked and inline CSS, rewrites the images found and minifies the CSS (med. risk)
    • rewrite_images – compresses and optomizes images (med. risk)
    • elide_attributes – removing attributes from tags (med. risk)
    • defer_javascript – combines JS and puts it at the end of your file (high risk AND experimental!)
    • move_css_to_head – combines CSS and moves it to the head of your file (low risk)

    Now keep in mind, not all of the features will work. While DreamHost is on a pretty cutting edge version of PageSpeed, they’re constantly innovating over there and improving. The best thing about these changes is, if you do it right, you can speed your site up faster than any plugin could do for you. And that? Is pretty cool right there.

  • CentOS and PHP 5.4

    CentOS and PHP 5.4

    PHP ElephantsI finally got around to PHP 5.4

    Alas this meant reinstalling certain things, like ImageMagick and APC.

    This also brought up the question of pagespeed, which I keep toying with. I use it at work, but since this server’s on CentOS with EasyApache, there’s no ‘easy’ way to install PageSpeed yet (not even a yum install will work), so it’s all manual work plus fiddling. I don’t mind installing ImageMagick and APC, but Google’s own ‘install from source’ aren’t really optimized for CentOS, even though they say they are, and I’m nervous about the matter. Well… I did it anyway. It’s at the bottom.

    The only reason I had to do this all over is that I moved to a new major version of PHP. If I’d stayed on 5.3 and up’d to 5.3.21, that wouldn’t have mattered. But this changed a lot of things, and thus, a reinstall.

    ImageMagick

    ImageMagick I started using ImageMagick shortly after starting with DreamHost, since my co-worker Shredder was working on the ‘Have WP support ImageMagick’ project. It was weird, since I remembered using it before, and then everyone moved to GD. I used to run a photo gallery with Gallery2, and it had a way to point your install to ImageMagick. Naturally I assumed I still had it on my server, since I used to (in 2008). Well since 2008, I’ve moved servers. Twice. And now it’s no longer default.

    Well. Let’s do one of the weirder installs.

    First you install these to get your dependancies:

    yum install ImageMagick
    yum install ImageMagick-devel
    

    Then you remove them, because nine times out of ten, the yum packages are old:

    yum remove ImageMagick
    yum remove ImageMagick-devel
    

    This also cleans out any old copies you may have, so it’s okay.

    Now we install ImageMagick latest and greatest from ImageMagick:

    cd ~/tmp/
    wget http://imagemagick.mirrorcatalogs.com/ImageMagick-6.8.1-10.tar.gz
    tar zxf ImageMagick-6.8.1-10.tar.gz
    cd ImageMagick-6.8.1-10
    ./configure --with-perl=/usr/bin/perl
    make
    make install
    

    Next we install the -devel again, but this time we tell it where from:

    rpm -i --nodeps http://www.imagemagick.org/download/linux/CentOS/x86_64/ImageMagick-devel-6.8.1-10.x86_64.rpm
    

    Finally we can install the PHP stuff. Since I’m on PHP 5.4, I have to use imagick-3.1.0RC2 – Normally I’m not up for RCs on my live server, but this is a case where if I want PHP 5.4, I have to. By the way, next time you complain that your webhost is behind on PHP, this is probably why. If they told you ‘To get PHP 5.4, I have to install Release Candidate products, so your website will run on stuff that’s still being tested,’ a lot of you would rethink the prospect.

    cd ~/tmp/
    wget http://pecl.php.net/get/imagick-3.1.0RC2.tgz
    tar zxf imagick-3.1.0RC2.tgz
    cd imagick-3.1.0RC2
    phpize
    ./configure
    make
    make install
    

    Next, edit your php.ini to add this:

    extension=imagick.so
    

    Restart httpd (service httpd restart) and make sure PHP is okay (php -v), and you should be done! I had to totally uninstall and start over to make it work, since I wasn’t starting from clean.

    Speaking of clean, cleanup is:

    yum remove ImageMagick-devel
    rm -rf ~/tmp/ImageMagick-6.8.1-10*
    rm -rf ~/tmp/imagick-3.1.0RC2*
    

    APC

    APCI love APC. I can use it for so many things, and I’m just more comfortable with it than xcache. Part of it stems from a feeling that if PHP built it, it’s more likely to work. Also it’s friendly with my brand of PHP, and after 15 years, I’m uninclined to change. I like DSO, even if it makes WP a bit odd.

    Get the latest version and install:

    cd ~/tmp/
    wget http://pecl.php.net/get/APC-3.1.14.tgz
    tar -xzf APC-3.1.14.tgz
    cd APC-3.1.14
    phpize
    ./configure
    make
    make install
    

    Add this to your php.ini:

    extension = "apc.so"
    

    Restart httpd again, clean up that folder, and then one more…

    mod_pagespeed

    mod_pagespeedI hate Google. Well, no I don’t, but I don’t trust them any more than I do Microsoft, and it’s really nothing personal, but I have issues with them. Now, I use PageSpeed at work, so I’m more comfortable than I was, and first I tried Google’s installer. The RPM won’t work, so I tried to install from source, but it got shirty with me, fast, and I thought “Why isn’t this as easy as the other two were!?” I mean, APC was stupid easy, and even easier than that would be yum install pagespeed right?

    Thankfully for my sanity, someone else did already figure this out for me, Jordan Cooks, and I’m reproducing his Installing mod_pagespeed on a cPanel/WHM server notes for myself.(By the way, I keep a copy of this article saved to DropBox since invariably I will half-ass this and break my site.) Prerequisite was to have mod_deflate, which I do.

    The commands are crazy simple:

    cd /usr/local/src
    mkdir mod_pagespeed
    cd mod_pagespeed
    wget https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-beta_current_x86_64.rpm
    rpm2cpio mod-pagespeed-beta_current_x86_64.rpm | cpio -idmv
    cp usr/lib64/httpd/modules/mod_pagespeed.so /usr/local/apache/modules/
    chmod 755 /usr/local/apache/modules/mod_pagespeed.so
    mkdir -p /var/mod_pagespeed/cache
    chown nobody:nobody /var/mod_pagespeed/*
    

    Once you do this, you have to edit the file, and this is where I differ from Jordan’s direction. He just copied this over /usr/local/apache/conf/pagespeed.conf but I had an older version from a ‘Let’s try Google’s way….’ attempt and someone else’s directions, so I made a backup and then took out the ModPagespeedGeneratedFilePrefix line since I know that’s deprecated. I also added in a line to tell it to ignore wp-admin.

    Here’s my pagespeed.conf (edited):

    LoadModule pagespeed_module modules/mod_pagespeed.so
    
    	# Only attempt to load mod_deflate if it hasn't been loaded already.
    <IfModule !mod_deflate.c>
    	LoadModule deflate_module modules/mod_deflate.so
    </IfModule>
    
    <IfModule pagespeed_module>
    	ModPagespeed on
    
    	AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER text/html
    
    	ModPagespeedFileCachePath "/var/mod_pagespeed/cache/"
    
        ModPagespeedEnableFilters rewrite_javascript,rewrite_css
        ModPagespeedEnableFilters collapse_whitespace,elide_attributes
        ModPagespeedEnableFilters rewrite_images
        ModPagespeedEnableFilters remove_comments
    
    	ModPagespeedFileCacheSizeKb 102400
    	ModPagespeedFileCacheCleanIntervalMs 3600000
    	
    	# Bound the number of images that can be rewritten at any one time; this
    	# avoids overloading the CPU. Set this to 0 to remove the bound.
    	#
    	# ModPagespeedImageMaxRewritesAtOnce 8
    
    	<Location /mod_pagespeed_beacon>
    		SetHandler mod_pagespeed_beacon
    	</Location>
    
    	<Location /mod_pagespeed_statistics>
    		Order allow,deny
    		# You may insert other "Allow from" lines to add hosts you want to
    		# allow to look at generated statistics. Another possibility is
    		# to comment out the "Order" and "Allow" options from the config
    		# file, to allow any client that can reach your server to examine
    		# statistics. This might be appropriate in an experimental setup or
    		# if the Apache server is protected by a reverse proxy that will
    		# filter URLs in some fashion.
    		Allow from localhost
    		Allow from 127.0.0.1
    		SetHandler mod_pagespeed_statistics
    	</Location>
    
    	ModPagespeedMessageBufferSize 100000	
    	ModPagespeedDisallow */wp-admin/*
    	ModPagespeedXHeaderValue "Powered By mod_pagespeed"
    
    	<Location /mod_pagespeed_message>
    		Allow from localhost
    		Allow from 127.0.0.1
    		SetHandler mod_pagespeed_message
    	</Location>
    	<Location /mod_pagespeed_referer_statistics>
    		Allow from localhost
    		Allow from 127.0.0.1
    		SetHandler mod_pagespeed_referer_statistics
    	</Location>
    </IfModule>
    

    To tell Apache to run this, edit /usr/local/apache/conf/includes/pre_main_global.conf and add:

    Include conf/pagespeed.conf

    Note: We put this code here because EasyApache and httpd.conf will eat your changes.

    Finally you rebuild Apache config and restart apache and test your headers to see goodness! My test was a success.

    HTTP/1.1 200 OK
    Date: Mon, 21 Jan 2013 03:12:13 GMT
    Server: Apache
    X-Powered-By: PHP/5.4.10
    Set-Cookie: PHPSESSID=f4bcdae48a1e5d5c5e8868cfef35593a; path=/
    Cache-Control: max-age=0, no-cache
    Pragma: no-cache
    X-Pingback: https://ipstenu.org/xmlrpc.php
    X-Mod-Pagespeed: Powered By mod_pagespeed
    Vary: Accept-Encoding
    Content-Length: 30864
    Content-Type: text/html; charset=UTF-8
    

    For those wondering why I’m ignoring wp-admin, well … sometimes, on some servers, in some setups, if you don’t do this, you can’t use the new media uploader. It appears that PageSpeed is compressing the already compressed JS files, and changing their names, which makes things go stupid. By adding in the following, I can avoid that:

    	ModPagespeedDisallow */wp-admin/*
    

    Besides, why do I need to cache admin things anyway, I ask you?

    So there you are! Welcome to PHP 5.4!

  • Dumping ms-files

    Dumping ms-files

    Trash Can On It's SideNOTE: You do not, under any circumstances, have to do this to continue using WordPress Multisite. I just wanted to see if I could.

    I have been toying around with this. Since WP 3.5 doesn’t use ms-files anymore, I wanted to see how much of a pain this would be. While I was at the DMV (I now have a California Drivers License), I started sketching out the methods and possibilities for how one might do this as safely as possible, without bolluxing up your site. I got about 90% successful with it, so this is something I would never suggest you do on a massive, live site. If you compare it to what I did to move images from uploads to blogs.dir for the main site, it’s far more complex and annoying.

    Do you have to do this? Again, no! Everything will work just fine. Can you do it? Sure. Why did I? Support. I already know all the mishegas with blogs.dir, but the new location, and it’s lack of ms-files, promises other, weird, errors. I want to repeat, you don’t need to do anything. The Core Team was fantastic with the work they did to turn off ms-files for fresh 3.5 and up installs is nothing short of phenomenal. This was just to assuage my curious nature, and learn more about the way things work.

    You ready? Here we go.

    I decided to move the images at http://test.ipstenu.org/ to start with, so everything will use that as my example. This is after I played with it on a local site and got my steps mostly solid. I knew that live is always different than test, so I backed up the DB first (always, always, backup first!) and went to town.

    Move the images

    This is obvious. Move your images from blogs.dir/SITENUM/files/ to /uploads/sites/SITENUM/ (or make an alias). I went and did it via command line in the /uploads/sites/ folder, doing this:

    $ cp -r ~/public_html/wp-content/blogs.dir/10/files .
    $ mv files 10
    

    Lather, rinse, repeat. I could have scripted it, but I was working out the kinks until I had two left.

    Edit all sites

    Upload Path, Upload URL Path and Fileupload Url. You can blank them all out.(Corrected thanks to Nacin.)

    Blank me out

    Since you’re blanking it out for everyone you can probably do this via SQL, but since I was doing the sites one at a time, I did them one at a time.

    Fix the Database
    Search/replace each posts table for each site, changing /files/ to /uploads/SITENUM/

    UPDATE wp_10_posts SET post_content = REPLACE (
    post_content,
    '=&quot;http://test.ipstenu.org/files/',
    '=&quot;http://test.ipstenu.org/wp-content/uploads/sites/10/');
    

    Full Trash, ColorWhy did I do it that way? Because of this blog. I talk a lot about code here, and I know I’ve talked about moving files around before. If you don’t do that, you’re okay with a less precise search, but why bother? This works, it’s safe, and I’d use it again.

    That got annoying really fast. I went and grabbed my favorite Search And Replace for WordPress (and any other database) tool. Seriously I love that. I used that to follow up, change everything, and it actually worked really well for me.

    Another DB Fix!

    One of the changes in 3.5 was turning off rewriting. This took me forever and a day to find. After I did that, my images showed up fine, but the little buggers kept uploading to /files! Turns out it was all because of the site option ms_files_rewriting

    The way I got around this was by putting the following in my wp-config.php file:

    define( 'UPLOADBLOGSDIR', 'wp-content/uploads/sites' );

    And then I ran this in SQL to turn off ms_files_rewriting. Or so I thought. More in a second.

    INSERT INTO `my_database`.`wp_sitemeta` (`meta_id`, `site_id`, `meta_key`, `meta_value`) VALUES (NULL, '1', 'ms_files_rewriting', '0');
    

    I came up with that after reading through /wp-includes/functions.php line 1515.

    For most sites, this worked, but in my later work, I determined that it actually wasn’t working. It was ignoring this. I don’t know why, but every test I did merrily ignored this setting, so I finally growled and wrote this mu-plugin function:

    function stupid_ms_files_rewriting() {
            $url = '/wp-content/uploads/sites/' . get_current_blog_id();
            define( 'BLOGUPLOADDIR', $url );
    }
    add_action('init','stupid_ms_files_rewriting');
    

    It’s stupid simple, it’s probably not a good idea, but it works for the three sites that have the stupids.

    Finish up .htaccess.

    .htaccess, remove the ms-files.php line for ms-files, or comment it out. This is pretty simple.

    Empty TrashWhy not move the main site to /uploads/?

    Because of the way I fixed the uploadblogsdir. It defaulted everyone to /sites/ and after an hour I said fuck it.

    Any weird problems?

    Yeah, two sites (this one and my grandmothers) decided that they wanted to be repetitious and spat out URLs like this: wp-content/uploads/sites/8/sites/8/

    Since that wasn’t right at all, and I was a little too eggnoggy to parse why, I did this:

    RewriteCond %{HTTP_HOST} ^taffys\.org
    RewriteRule ^wp-content/uploads/sites/8/sites/8/(.*) /wp-content/uploads/sites/8/$1 [L,R=301]
    

    I swear I have no idea why three sites got stuck with /files/ and two more decided to double down, Vegas style, but frankly I’m pleased I got through this far on my own.

    I can’t stress enough that you do not have to do this!