|
| 1 | +# .htaccess Snippets |
| 2 | +A collection of useful .htaccess, all in one place. I decided to create this repo after getting so tired (and bored) with Googling everytime there's a need of forcing `www` for my new website. |
| 3 | + |
| 4 | +**Disclaimer**: While dropping the snippet into an `.htaccess` file is most of the time sufficient, there are cases when certain modifications might be required. Use with your own risks. |
| 5 | + |
| 6 | +## Table of Contents |
| 7 | +- [Rewrite and Redirection](#rewrite-and-redirection) |
| 8 | +- [Security](#security) |
| 9 | +- [Performance](#performance) |
| 10 | +- [Miscellaneous](#miscellaneous) |
| 11 | + |
| 12 | +## Rewrite and Redirection |
| 13 | +Note: It is assumed that you have `mod_rewrite` installed and enabled. |
| 14 | + |
| 15 | +### Force www |
| 16 | +``` apacheconf |
| 17 | +RewriteEngine on |
| 18 | +RewriteCond %{HTTP_HOST} ^example\.com [NC] |
| 19 | +RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301,NC] |
| 20 | +``` |
| 21 | + |
| 22 | +### Force www in a Generic Way |
| 23 | +``` apacheconf |
| 24 | +RewriteCond %{HTTP_HOST} !^$ |
| 25 | +RewriteCond %{HTTP_HOST} !^www\. [NC] |
| 26 | +RewriteCond %{HTTPS}s ^on(s)| |
| 27 | +RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] |
| 28 | +``` |
| 29 | +This works for _any_ domain. [Source](https://stackoverflow.com/questions/4916222/htaccess-how-to-force-www-in-a-generic-way) |
| 30 | + |
| 31 | +### Force non-www |
| 32 | +It's actually [recommended](http://no-www.org/) to remove `www` from your domain. Surprise surprise! |
| 33 | +``` apacheconf |
| 34 | +RewriteEngine on |
| 35 | +RewriteCond %{HTTP_HOST} ^www\.example\.com [NC] |
| 36 | +RewriteRule ^(.*)$ http://example.com/$1 [L,R=301] |
| 37 | +``` |
| 38 | + |
| 39 | +### Force HTTPS |
| 40 | +``` apacheconf |
| 41 | +RewriteEngine on |
| 42 | +RewriteCond %{HTTPS} !on |
| 43 | +RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} |
| 44 | +``` |
| 45 | + |
| 46 | +### Force Trailing Slash |
| 47 | +``` apacheconf |
| 48 | +RewriteCond %{REQUEST_URI} /+[^\.]+$ |
| 49 | +RewriteRule ^(.+[^/])$ %{REQUEST_URI}/ [R=301,L] |
| 50 | +``` |
| 51 | + |
| 52 | +### Redirect a Single Page |
| 53 | +``` apacheconf |
| 54 | +Redirect 301 /oldpage.html http://www.yoursite.com/newpage.html |
| 55 | +Redirect 301 /oldpage2.html http://www.yoursite.com/folder/ |
| 56 | +``` |
| 57 | +[Source](http://css-tricks.com/snippets/htaccess/301-redirects/) |
| 58 | + |
| 59 | +### Redirect an Entire Site |
| 60 | +``` apacheconf |
| 61 | +Redirect 301 / http://newsite.com/ |
| 62 | +``` |
| 63 | +This way does it with links intact. That is `www.oldsite.com/some/crazy/link.html` will become `www.newsite.com/some/crazy/link.html`. This is extremely helpful when you are just "moving" a site to a new domain. [Source](http://css-tricks.com/snippets/htaccess/301-redirects/) |
| 64 | + |
| 65 | +## Security |
| 66 | +### Deny All Access |
| 67 | +``` apacheconf |
| 68 | +Deny from All |
| 69 | +``` |
| 70 | + |
| 71 | +But wait, this will lock you out from your content as well! Thus introducing... |
| 72 | + |
| 73 | +### Deny All Access Except Yours |
| 74 | +``` apacheconf |
| 75 | +Order deny, allow |
| 76 | +Deny from All |
| 77 | +Allow from xxx.xxx.xxx.xxx |
| 78 | +``` |
| 79 | +`xxx.xxx.xxx.xxx` is your IP. If you replace the last three digits with 0/12 for example, this will specify a range of IPs within the same network, thus saving you the trouble to list all allowed IPs separately. [Source](http://speckyboy.com/2013/01/08/useful-htaccess-snippets-and-hacks/) |
| 80 | + |
| 81 | +Now of course there's a reversed version: |
| 82 | + |
| 83 | +### Allow All Access Except Spammers' |
| 84 | +``` apacheconf |
| 85 | +Order deny, allow |
| 86 | +Allow from All |
| 87 | +Deny from xxx.xxx.xxx.xxx |
| 88 | +Deny from xxx.xxx.xxx.xxy |
| 89 | +``` |
| 90 | + |
| 91 | +### Deny Access to Hidden Files and Directories |
| 92 | +Hidden files and directories (those whose names start with a dot `.`) should most, if not all, of the time be secured. For example: `.htaccess`, `.htpasswd`, `.git`, `.hg`... |
| 93 | +``` apacheconf |
| 94 | +RewriteCond %{SCRIPT_FILENAME} -d [OR] |
| 95 | +RewriteCond %{SCRIPT_FILENAME} -f |
| 96 | +RewriteRule "(^|/)\." - [F] |
| 97 | +``` |
| 98 | + |
| 99 | +### Disable Directory Browsing |
| 100 | +``` apacheconf |
| 101 | +Options All -Indexes |
| 102 | +``` |
| 103 | + |
| 104 | +### Disable Image Hotlinking |
| 105 | +``` apacheconf |
| 106 | +RewriteEngine on |
| 107 | +RewriteCond %{HTTP_REFERER} !^$ |
| 108 | +RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourdomain.com [NC] |
| 109 | +RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L] |
| 110 | +``` |
| 111 | + |
| 112 | +### Password Protect a Directory |
| 113 | +First you need to create a `.htpasswd` file somewhere in the system: |
| 114 | +``` bash |
| 115 | +htpasswd -c /home/fellowship/.htpasswd boromir |
| 116 | +``` |
| 117 | + |
| 118 | +Then you can use it for authentication: |
| 119 | +``` apacheconf |
| 120 | +AuthType Basic |
| 121 | +AuthName "One does not simply" |
| 122 | +AuthUserFile /home/fellowship/.htpasswd |
| 123 | +Require valid-user |
| 124 | +``` |
| 125 | + |
| 126 | +## Performance |
| 127 | +### Compress Text Files |
| 128 | +``` apacheconf |
| 129 | +<IfModule mod_deflate.c> |
| 130 | +
|
| 131 | + # Force compression for mangled headers. |
| 132 | + # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping |
| 133 | + <IfModule mod_setenvif.c> |
| 134 | + <IfModule mod_headers.c> |
| 135 | + SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding |
| 136 | + RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding |
| 137 | + </IfModule> |
| 138 | + </IfModule> |
| 139 | +
|
| 140 | + # Compress all output labeled with one of the following MIME-types |
| 141 | + # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` |
| 142 | + # and can remove the `<IfModule mod_filter.c>` and `</IfModule>` lines |
| 143 | + # as `AddOutputFilterByType` is still in the core directives). |
| 144 | + <IfModule mod_filter.c> |
| 145 | + AddOutputFilterByType DEFLATE application/atom+xml \ |
| 146 | + application/javascript \ |
| 147 | + application/json \ |
| 148 | + application/rss+xml \ |
| 149 | + application/vnd.ms-fontobject \ |
| 150 | + application/x-font-ttf \ |
| 151 | + application/x-web-app-manifest+json \ |
| 152 | + application/xhtml+xml \ |
| 153 | + application/xml \ |
| 154 | + font/opentype \ |
| 155 | + image/svg+xml \ |
| 156 | + image/x-icon \ |
| 157 | + text/css \ |
| 158 | + text/html \ |
| 159 | + text/plain \ |
| 160 | + text/x-component \ |
| 161 | + text/xml |
| 162 | + </IfModule> |
| 163 | +
|
| 164 | +</IfModule> |
| 165 | +``` |
| 166 | +[Source](https://h5bp.com) |
| 167 | + |
| 168 | + |
| 169 | +### Set Expires Headers |
| 170 | +_Expires headers_ tell the browser whether they should request a specific file from the server or just grab it from the cache. It is advisable to set static content's expires headers to something far in the future. |
| 171 | +If you don't control versioning with filename-based cache busting, consider lowering the cache time for resources like CSS and JS to something like 1 week. [Source](http://h5bp.com) |
| 172 | +``` apacheconf |
| 173 | +<IfModule mod_expires.c> |
| 174 | + ExpiresActive on |
| 175 | + ExpiresDefault "access plus 1 month" |
| 176 | +
|
| 177 | + # CSS |
| 178 | + ExpiresByType text/css "access plus 1 year" |
| 179 | +
|
| 180 | + # Data interchange |
| 181 | + ExpiresByType application/json "access plus 0 seconds" |
| 182 | + ExpiresByType application/xml "access plus 0 seconds" |
| 183 | + ExpiresByType text/xml "access plus 0 seconds" |
| 184 | +
|
| 185 | + # Favicon (cannot be renamed!) |
| 186 | + ExpiresByType image/x-icon "access plus 1 week" |
| 187 | +
|
| 188 | + # HTML components (HTCs) |
| 189 | + ExpiresByType text/x-component "access plus 1 month" |
| 190 | +
|
| 191 | + # HTML |
| 192 | + ExpiresByType text/html "access plus 0 seconds" |
| 193 | +
|
| 194 | + # JavaScript |
| 195 | + ExpiresByType application/javascript "access plus 1 year" |
| 196 | +
|
| 197 | + # Manifest files |
| 198 | + ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" |
| 199 | + ExpiresByType text/cache-manifest "access plus 0 seconds" |
| 200 | +
|
| 201 | + # Media |
| 202 | + ExpiresByType audio/ogg "access plus 1 month" |
| 203 | + ExpiresByType image/gif "access plus 1 month" |
| 204 | + ExpiresByType image/jpeg "access plus 1 month" |
| 205 | + ExpiresByType image/png "access plus 1 month" |
| 206 | + ExpiresByType video/mp4 "access plus 1 month" |
| 207 | + ExpiresByType video/ogg "access plus 1 month" |
| 208 | + ExpiresByType video/webm "access plus 1 month" |
| 209 | +
|
| 210 | + # Web feeds |
| 211 | + ExpiresByType application/atom+xml "access plus 1 hour" |
| 212 | + ExpiresByType application/rss+xml "access plus 1 hour" |
| 213 | +
|
| 214 | + # Web fonts |
| 215 | + ExpiresByType application/font-woff "access plus 1 month" |
| 216 | + ExpiresByType application/vnd.ms-fontobject "access plus 1 month" |
| 217 | + ExpiresByType application/x-font-ttf "access plus 1 month" |
| 218 | + ExpiresByType font/opentype "access plus 1 month" |
| 219 | + ExpiresByType image/svg+xml "access plus 1 month" |
| 220 | +</IfModule> |
| 221 | +``` |
| 222 | + |
| 223 | +### Turn eTags Off |
| 224 | +By removing the ETag header, you disable caches and browsers from being able to validate files, so they are forced to rely on your Cache-Control and Expires header. [Source](http://www.askapache.com/htaccess/apache-speed-etags.html) |
| 225 | +``` apacheconf |
| 226 | +<IfModule mod_headers.c> |
| 227 | + Header unset ETag |
| 228 | +</IfModule> |
| 229 | +FileETag None |
| 230 | +``` |
| 231 | + |
| 232 | + |
| 233 | +## Miscellaneous |
| 234 | + |
| 235 | +### Set PHP Variables |
| 236 | +``` apacheconf |
| 237 | +php_value <key> <val> |
| 238 | +
|
| 239 | +# For example: |
| 240 | +php_value upload_max_filesize 50M |
| 241 | +php_value max_execution_time 240 |
| 242 | +``` |
| 243 | + |
| 244 | +### Custom Error Pages |
| 245 | +``` apacheconf |
| 246 | +ErrorDocument 400 /errors/breakingbad.html |
| 247 | +ErrorDocument 401 /errors/notrespassing.html |
| 248 | +ErrorDocument 403 /errors/mordor.html |
| 249 | +ErrorDocument 404 /errors/halflife3.html |
| 250 | +ErrorDocument 500 /errors/notabugitsafeature.html |
| 251 | +``` |
| 252 | + |
| 253 | +### Force Downloading |
| 254 | +Sometimes you want to force the browser to download some content instead of displaying it. The following snippet will help. |
| 255 | +``` apacheconf |
| 256 | +<Files *.md> |
| 257 | + ForceType application/octet-stream |
| 258 | + Header set Content-Disposition attachment |
| 259 | +</Files> |
| 260 | +``` |
| 261 | + |
| 262 | +### Allow Cross-Domain Fonts |
| 263 | +CDN-served webfonts might not work in Firefox or IE due to [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing). The following snippet from [HTML5Boilerplate](http://h5bp.com) should make it happen. |
| 264 | +``` apacheconf |
| 265 | +<IfModule mod_headers.c> |
| 266 | + <FilesMatch "\.(eot|otf|ttc|ttf|woff)$"> |
| 267 | + Header set Access-Control-Allow-Origin "*" |
| 268 | + </FilesMatch> |
| 269 | +</IfModule> |
| 270 | +``` |
0 commit comments