Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.htaccess in Multiple Environments

I know similar questions have been asked before but I haven't found any really specific answers for my situation.

I have an ExpressionEngine site running on multiple environments (local, dev, production), and each of those environments needs different .htaccess rules:

All Environments

  • Remove index.php
  • Set a 404 file
  • Set 301 Redirects

Development

  • Password Protect with .htpasswd
  • Force HTTPS protocol
  • Prevent search engine indexing with X-Robots-Tag

Production

  • Force HTTPS protocol
  • Redirect non-www subdomains to www

Local

  • No unique rules.

I've seen a lot of examples of how you can set specific environments per-module. Like RewriteCond %{REQUEST_HOST} ^dev.myurl.com for the mod_rewrite module, and tricks like this for .htpasswd requirements.

But what I would really prefer is some way to set global environment variables, then re-use those variables in the .htaccess file per-environment. To use pseudo-javascript as an example, something like:

var local = 'mysite.local';
var development = 'dev.mysite.com';
var production = 'www.mysite.com';

// Global .htaccess rules

if(environment == local){
   // Local environment .htaccess rules
}

if(environment == development){
   // Development environment .htaccess rules
}

if(environment == production){
   //Production envirotnment .htaccess rules
}

This way all of the environment-specific rules are all grouped together, making a really clean file, and only one variable needs to be changed if an environment is changed.

I've seen a few references to altering settings in Apache's config files, but obviously that's not a viable option if I'm dealing with 3rd-party hosts.

So is this pie-in-the-sky wishful thinking, or can it be done?

like image 370
kmgdev Avatar asked Oct 14 '12 21:10

kmgdev


2 Answers

Jon's answer is a good one. Unfortunately, not all web hosts will allow you to control that -D parameter for starting Apache.

Here's a way to use a single htaccess file on dev and production, but only have the dev site password protected:

# ----------------------------------------------------------------------
# Password protect staging server
# Use one .htaccess file across multiple environments
# (e.g. local, dev, staging, production)
# but only password protect a specific environment.
# ----------------------------------------------------------------------

SetEnvIf Host staging.domain.com passreq
AuthType Basic
AuthName "Password Required"
AuthUserFile /full/path/to/.htpasswd
Require valid-user
Order allow,deny
Allow from all
Deny from env=passreq
Satisfy any
like image 72
Jason Siffring Avatar answered Sep 28 '22 05:09

Jason Siffring


So is this pie-in-the-sky wishful thinking, or can it be done?

IMO, yes. You're never going to be able to get predictable "scoping" of rules based on ENV variables or anything like that. There doesn't exist arbitrary if(something) { do everything in here } in apache. Lots of directives won't work inside certain scopes, and in later on, when you need to change how something works, you're more likely to break what you have than simply amending it.

The best way is to not use htaccess files at all:

You should avoid using .htaccess files completely if you have access to httpd main server config file. Using .htaccess files slows down your Apache http server. Any directive that you can include in a .htaccess file is better set in a Directory block, as it will have the same effect with better performance.

Create a separate vhost for local, dev, and production. Turn them on or off as needed, whatever global config they share, store that elsewhere (like in a file called global.includes) and then use the Include directive in all 3 vhosts. If you need to apply rules to specific directories, use the <Directory> block instead of htaccess files.

If you'd rather stick everything inside htaccess files, you could try putting everything in <IfDefine> blocks, it's probably the closest thing you'll have to your pseudo-code in your question. Essentially something like:

# Global htaccess rules
RewriteEngine On
RewriteRule ^foo$ /bar [L]

# Only local
<IfDefine LocalInstance>
    RewriteRule ^local/foo /bar [L]
</IfDefine>

# Only dev
<IfDefine DevInstance>
    RewriteRule ^dev/foo /bar [L]
</IfDefine>

# Only production
<IfDefine ProductionInstance>
    RewriteRule ^dev/foo /bar [L]
</IfDefine>

Then when you start apache, you'd need to pass in -DLocalInstance, -DDevInstance, or -DProductionInstance as command line paramaeters or using the Define directive (with only one argument) somewhere in your vhost config. This isn't guaranteed to work as smoothly as it looks like it should, I've ran into unexplained issues with <IfDefine> before, especially if you try to get too fancy.

like image 23
Jon Lin Avatar answered Sep 28 '22 07:09

Jon Lin