This came up back in April in the comments of WordPress Login Protection With .htaccess, where I remarked my .htaccess was pretty long and weird. It came up again when I was doing a MeetWP presentation about hacked sites and some security.
So what is it? Actually less long and weird these days. I’ve been trimming stuff out. But since people ask, here it is, broken out into ‘chunks.’
Security
Everything in this section is for security purposes. That is, I feel it helps my site be safer.
# Tinfoil Hat Stuff Options +Includes Options +FollowSymLinks -Indexes
This is basic .htaccess stuff, says to allow includes and symlinks, but stop indexes. This means if you go to halfelf.org/wp-content/uploads/
you don’t see anything, even if I don’t have an index file.
### Blocking Spammers Section ### <files wp-config.php> order allow,deny deny from all </files>
Now we’re into a little odder bits. This stops anyone from surfing to my wp-config.php file. It shouldn’t matter, PHP won’t let it load the content, but if my PHP is off, it protects me just in case!
# Stop protected folders from being narked. Also helps with spammers ErrorDocument 401 /401.html
This is because of the next section. It gives a nice error for 401s, which WP normally gets gitty over. And not the fun way.
<IfModule mod_rewrite.c> # Stop spam attack logins and comments RewriteEngine On RewriteCond %{REQUEST_METHOD} POST RewriteCond %{REQUEST_URI} .(wp-comments-post|wp-login)\.php* RewriteCond %{HTTP_REFERER} !.*(ipstenu.org|halfelf.org|ipstenu.org|otherplace.net).* [OR] RewriteCond %{HTTP_USER_AGENT} ^$ RewriteRule (.*) http://%{REMOTE_ADDR}/$ [R=301,L] # SVN & Git protection RewriteRule ^(.*/)?(\.svn|\.git)/ - [F,L] ErrorDocument 403 "Access Forbidden" </ifModule>
Ahhh, yes. Here I say “If you’re coming to wp-comments-post OR wp-login and you are NOT refereed by one of my domains, sod off.” And then it says “Oh and if you’re looking for .svn or .git files? Go away.” This isn’t perfect, but it works for some of the botnets. The fun part is that the rewrite sends them back to themselves, which should cause annoying things to happen. Don’t want that? Redirect them to fbi.gov. Actually, if some tool had a page “Redirect botnets here…” I would use that, but generally I send them to http://lmgtfy.com/?q=wordpress+botnet
because I’m that sort of kid.
Speed and Bandwidth
Now that I’m safer, lets speed this stuff up!
<IfModule mod_rewrite.c> RewriteEngine on # ultimate hotlink protection RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{REQUEST_FILENAME} -f RewriteCond %{REQUEST_FILENAME} \.(gif|jpe?g?|png)$ [NC] RewriteCond %{HTTP_REFERER} !^https?://([^.]+\.)?(ipstenu.org|taffys.org|halfelf.org|poohnau.us|ipstenu.org) [NC] RewriteRule \.(gif|jpe?g?|png)$ - [F,NC,L] </ifModule>
First up, stop the hotlinks! I got the idea from Perishable Press, and it stops you from embedding my images. This means my site is faster, as you’re not sucking up my bandwidth. I get 5G so it’s not too much of a concern right now, but it’s the principle of the thing. Don’t hotlink images!
### Caching Section ### # mod_pagespeed <IfModule pagespeed_module> ModPagespeed on ModPagespeedEnableFilters defer_javascript,combine_javascript,move_css_to_head,insert_dns_prefetch,insert_image_dimensions,inline_preview_images,resize_mobile_images ModPagespeedDisallow */FOLDERNAME/* ModPagespeedEnableFilters insert_ga ModPagespeedAnalyticsID UA-MYCODE-4 </IfModule>
I use Pagespeed on my server, so here I’ve added in my extra rules. Not everything is active for all sites. This is my default WP rule-set though, and it works well. I have it skipping a couple non WP folders, who have their own rules inside on their own .htaccess files anyway. If you don’t have pagespeed? Skip this section.
# Expired <IfModule mod_expires.c> <Filesmatch "\.(jp?eg|png|gif|ico|woff)$"> ExpiresActive on ExpiresDefault "access 1 year" </Filesmatch> <Filesmatch "\.(css|js|swf|mov|mp3|mpeg|mp4|ogg|ogv|ttf|xml|svg|html)$"> ExpiresActive on ExpiresDefault "access 1 month" </Filesmatch> ExpiresDefault "access 2 days" </IfModule> ## END EXPIRES ##
Oy. There are a couple ways you can control all these things. One is the way I did (filesmatch) and the other is ExpiresByType image/jpg "access plus 1 year"
. Is one better than they other? I don’t know. Not that I’ve managed to see, but I find the filesmatch to be easier to read and add things too. It’s shorter. Does that make it better? Only in so far as my management goes.
#Gzip <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/text text/html text/plain text/xml text/css application/x-javascript application/javascript text/javascript font/opentype font/truetype font/eot application/x-font-ttf </IfModule> #End Gzip
Finally we have gzip, which compresses and makes things smaller and thus faster. Using gzip saves me about 80% in filesize, so it makes things faster to download and, thus, display. If you’re using this in your .htaccess, do not also try to use it in plugins/extensions for other web apps, that way likes double compression and garbage on your pages.
Add support!
This is a really short bit to add in support for filetypes I use that aren’t always standard:
# Add filetypes AddType application/x-mobipocket-ebook mobi AddType application/epub+zip epub .epub AddType video/ogg .ogv AddType video/mp4 .mp4 AddType video/webm .webm
Rewrites
In general, this is useless to everyone else, save as an example.
### Massive Redirect Section! ### <IfModule mod_rewrite.c> RewriteEngine On # Apple Touch Icons RewriteRule ^(.*)-precomposed.png /code/images/apple/$1.png [L,R=301] RewriteRule ^apple-touch-icon(.*) /code/images/apple/apple-touch-icon$1 [L,R=301] # Ipstenu Moves RewriteCond %{HTTP_HOST} ^blog\.ipstenu\.org RewriteRule ^(.*) https://ipstenu.org/$1 [L,R=301] RewriteCond %{HTTP_HOST} ^ipstenu\.org RewriteRule ^blog/([0-9]{4})/([0-9]{2})/(.*)$ https://ipstenu.org/$1/$3 [L,R=301] RewriteCond %{HTTP_HOST} ^ipstenu\.org RewriteRule ^blog/(.*)$ https://ipstenu.org/$1 [L,R=301] RewriteCond %{HTTP_HOST} ^ipstenu\.org RewriteRule ^wp-content/blogs.dir/1/files/(.*)$ https://ipstenu.org/wp-content/uploads/sites/1/$1 [L,R=301] RewriteCond %{HTTP_HOST} ^ipstenu\.org RewriteRule ^(.*)favicon.(ico|png)$ /code/images/favicons/ipstenu.ico [L,R=301] [NB: This sort of thing is duplicated for other domains which also had things moved around] </IfModule>
I cut out a couple of sections, where what I did with the ‘Only redirect ipstenu.org’ stuff is repeated for each site. I built much of that after reading my 404 logs and determining what needed to be redirected. Everything is commented and in logical sections, so I can easily find and remember what the heck I was doing.
This is the longest section, the stuff under # Ipstenu Moves
and such, because they’re accounting for files that moved a million years ago. But the moves section is pretty straightforward too, as you can see where things went. I try to keep it as compact as I can. Sometimes I go through and make them more and more efficient, as I learn new tricks.
What used to be here?
I used to include the 5G Blacklist 2013 and/or the 2013 User Agent Blacklist (or whatever the current versions are), but now I don’t have it on all my sites because of the work I’ve been putting in on my firewall and ModSecurity instead. Every once in a while, someone tells me I’m putting too much work on Apache to handle the hackers and spammers, and I generally reply “Better Apache than WordPress.”
Regularly, I go through my .htaccess and see what I can push over to ModSec. I also trim down my PageSpeed rules into things that work on most sites, things that only work on this site, and things that work for everything. This is why there’s no blacklisting here, it’s all handled by my firewall and mod_security and that’s that. I like to take the load off of apache and .htaccess and PHP and make the server do the work.
Why not nginx?
Per site configuration is still a pill. No, really that’s it. It’s a hassle to re-do everything I have in htaccess, I can’t just toss a .nginx file in there on the fly without restarting nginx, which means for shared hosts it’s just not gonna happen. Sucks. For managed hosting, where you don’t allow users to make those changes, sure, it’s great. But that’s not my use case. I may use it for a Varnish in front server one day, after I rebuild everything from scratch.
Comments
13 responses to “My Super Secret .htaccess File”
Wow, that seems to be a lot of useful stuff. I have to go through this.
Does it work for Multisites too?
@Lars Mielke: Yep π I’m using it on this site (which is Multisite) today.
I need to do some work with my .htaccess file again. Have implemented a couple of these things, but need to add MOAR.
Thanks for sharing this! π
Hmm, in my opinion, bots such as googlebot should be allowed to directly access your images, in your hotlink protection section.
@Marcu Alin: A lot of people feel that way. You can totally add Google to this line to do that:
RewriteCond %{HTTP_REFERER} !^https?://([^.]+\.)?(ipstenu.org|taffys.org|halfelf.org|poohnau.us|ipstenu.org) [NC]
But…. I don’t. I don’t think hotlinking is ever a good idea. And I really don’t like the changes Google made to their image search. See https://halfelf.org/2013/hotlinking-google/ for my rant.
Thanks for sharing this.
For people who don’t know enough to use ModSecurity or their web host won’t let them use it, would you still recommend Perishable Press’ Blacklist?
What about the below? What does it do exactly? And should it stay in the .htaccess?
@Yael K. Miller: That block you quoted is WordPress π
http://codex.wordpress.org/htaccess
It changes based on Multisite, install location (like giving WP it’s own folder, which I do), and plugins, but in essence, without that, we don’t get pretty permailnks. It’s safe as houses π
And if my host doesn’t have modSecurity, I leave. If you can’t, though, yes, use PerishablePress’s list. I laud it for it’s functionality.
@Ipstenu (Mika Epstein):
Cool, thanks.
This is a great tutorial, I wasn’t aware that I can do that much with .htaccess. I will try some of the tricks to see how it can increase my security and help my site.
Actually, you can also use the nginx -s reload command. For solely checking the configuration without restarting or reloading anything, there is nginx -t.
You could just put this entire part of the config in a separate file and use the include directive to add it to all the sites you need it on. What’s more, you can just write a script to add it to every server block, or right below any line containing both the server_names directive and the name of the website you intend to add it to.
That is, exactly, what I consider to be “a pill” though! Everything that is built in with apache is intentionally not in nginx for the sake of speed. I totally get why, bit I’m just not really ready for it with all my apps. (Boilerplate reminder – this server is not JUST WordPress π not all of the apps have nginx confs readily available)
I usually just put wp-config up in /home instead of public_html as WP will look one directory up for it.
@Keith: I use ‘Giving WP it’s own directory’ so often, that’s unreliable for me.