1

I have a webpage template that I use to run multiple landing pages and htaccess is used to direct the friendly URL to the pageid within the PHP code. It all works but at the moment I have to add the rewrite condition line before every rule or the redirects clash. You can see this in the pseudo code below:

RewriteEngine On

RewriteCond for domain1
RewriteRule for page 1 on domain 1
RewriteCond for domain1
RewriteRule for page 2 on domain 1

RewriteCond for domain2
RewriteRule for page 1 on domain 2
RewriteCond for domain2
RewriteRule for page 2 on domain 2

This doesn't scale well and if I was doing this any other languages I would be grouping the rules like this:

RewriteCond for domain1
{
    RewriteRule for page 1 on domain 1  
    RewriteRule for page 2 on domain 1
}

RewriteCond for domain2
{
    RewriteRule for page 1 on domain 2  
    RewriteRule for page 2 on domain 2
}

If I could group, it would make the htaccess file much more manageable. Is there a way to group rules? I have tried searching for a solution but every example I come across discusses 1 redirect for 1 domain. My other idea is to make an htaccess script in PHP / MySQL which I enter my data, press a button and it writes the htaccess file, it would be complex to read but would ok. I'm sure there is a simple solution. Can anyone help please?

MrWhite
  • 43,179
  • 8
  • 60
  • 84
PlatformDating
  • 167
  • 1
  • 13
  • 2
    On Apache 2.4, putting the RewriteRules into `` containers that check the host name should work, I suppose. https://httpd.apache.org/docs/2.4/expr.html#examples – CBroe Apr 16 '20 at 10:26

2 Answers2

1

Here is my final solution that is working in produciton:

RewriteEngine On

<If "%{HTTP_HOST} == 'domain1.com'">        

        #main pages
        RewriteRule /members/$    /index.php?pageid=2 [NC,L]
        RewriteRule /contact/$    /index.php?pageid=3 [NC,L]
        RewriteRule /affiliates/$ /index.php?pageid=4 [NC,L]

        #blog articles
        RewriteRule /welcome-to-our-blog/$ /index.php?pageid=b0001 [NC,L]
</If>


<If "%{HTTP_HOST} == 'domain2.com'">        

        #main pages
        RewriteRule /about/$        /index.php?pageid=2 [NC,L]
        RewriteRule /press-kit/$    /index.php?pageid=3 [NC,L]
        RewriteRule /contact/$   /index.php?pageid=4 [NC,L]
        RewriteRule /affiliates/$   /index.php?pageid=5 [NC,L]

        #blog articles
        RewriteRule /welcome-to-our-blog/$ /index.php?pageid=b0001 [NC,L]
</If>
PlatformDating
  • 167
  • 1
  • 13
  • Just curious how this is actually working... as you appear to have conflicts, eg. `/index.php?pageid=2` returns the `/members/` page for `domain1`, but the `/about/` page for `domain2`? Are you checking the `HTTP_HOST` in your `index.php` script as well? Or are these in fact different `index.php` files (since the document root for each domain might point to different subdirectory's and this `.htaccess` file is _inherited_ from a common parent directory?) – MrWhite Sep 22 '20 at 01:59
  • @MrWhite Yes, the host is detected within the index script and calls an include for each domain. It runs 16 sites at the moment so makes it more manageable to break it up and allow each site to have its own structure. – PlatformDating Oct 05 '20 at 13:28
0

A few alternatives to using <If> sections, since <If> is not without its caveats.

  1. Skip directives when the condition does (or does not) match
  2. Prefix the URL (to match) with the hostname
  3. Separate .htaccess config file for each host

(Aside: Do you really want a case-insensitive - NC - match?)

1. Skip directives when the condition does (or does not) match

You can skip (S flag) any number of directives when the rule (or condition) does not match. ie. If you skip the rules that follow when the HTTP_HOST is not domain1.com then the following rules are only processed when the HTTP_HOST is domain1.com.

For example:

# Skip the following 4 rules when the host is not "domain1.com"
RewriteCond %{HTTP_HOST} !=domain1.com
RewriteRule ^ - [S=4]

# Only processed when the HTTP_HOST is equal to "domain1.com"

#main pages
RewriteRule ^members/$     /index.php?pageid=2 [NC,L]
RewriteRule ^contact/$     /index.php?pageid=3 [NC,L]
RewriteRule ^affiliates/$  /index.php?pageid=4 [NC,L]

#blog articles
RewriteRule ^welcome-to-our-blog/$ /index.php?pageid=b0001 [NC,L]

# --------------------------------------------------------------------

# Skip the following 5 rules when the host is not "domain2.com"
RewriteCond %{HTTP_HOST} !=domain2.com
RewriteRule ^ - [S=5]

# Only processed when the HTTP_HOST is equal to "domain2.com"

#main pages
RewriteRule ^about/$       /index.php?pageid=2 [NC,L]
RewriteRule ^press-kit/$   /index.php?pageid=3 [NC,L]
RewriteRule ^contact/$     /index.php?pageid=4 [NC,L]
RewriteRule ^affiliates/$  /index.php?pageid=5 [NC,L]

#blog articles
RewriteRule ^welcome-to-our-blog/$ /index.php?pageid=b0001 [NC,L]

Although an obvious caveat with this approach is that you need to be careful to update the S=<number> flag when adding or removing any rules.

You can obviously reverse the logic to have what looks like a table-of-contents at the top. For example:

RewriteCond %{HTTP_HOST} =domain1.com
RewriteRule ^ - [S=2]
RewriteCond %{HTTP_HOST} =domain2.com
RewriteRule ^ - [S=5]
RewriteCond %{HTTP_HOST} =domain3.com
RewriteRule ^ - [S=9]

# --------------------------------------------------------------------
# domain1.com

#main pages
RewriteRule ^members/$     /index.php?pageid=2 [NC,L]
RewriteRule ^contact/$     /index.php?pageid=3 [NC,L]
RewriteRule ^affiliates/$  /index.php?pageid=4 [NC,L]

#blog articles
RewriteRule ^welcome-to-our-blog/$ /index.php?pageid=b0001 [NC,L]

# --------------------------------------------------------------------
# domain2.com

#main pages
RewriteRule ^about/$       /index.php?pageid=2 [NC,L]
RewriteRule ^press-kit/$   /index.php?pageid=3 [NC,L]
RewriteRule ^contact/$     /index.php?pageid=4 [NC,L]
RewriteRule ^affiliates/$  /index.php?pageid=5 [NC,L]

#blog articles
RewriteRule ^welcome-to-our-blog/$ /index.php?pageid=b0001 [NC,L]

Although the "skip" rules are potentially more prone to error since you may need to update several when adding or removing rules, depending on where these rules fall.

2. Prefix the URL (to match) with the hostname

Alternatively, you could (temporarily) prefix the URL-path with the hostname and match against this in the rules that follow. This avoids the additional RewriteCond directive in all subsequent rules.

For example:

# Prefix URL-path with HTTP_HOST if not already prefixed / No "L" flag
RewriteCond %{HTTP_HOST}@$1 !^([a-z.-]+)@\1
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) %{HTTP_HOST}/$1

# --------------------------------------------------------------------
# domain1.com

#main pages
RewriteRule ^domain1\.com/members/$     /index.php?pageid=2 [NC,L]
RewriteRule ^domain1\.com/contact/$     /index.php?pageid=3 [NC,L]
RewriteRule ^domain1\.com/affiliates/$  /index.php?pageid=4 [NC,L]

#blog articles
RewriteRule ^domain1\.com/welcome-to-our-blog/$ /index.php?pageid=b0001 [NC,L]

# --------------------------------------------------------------------
# domain2.com

#main pages
RewriteRule ^domain2\.com/about/$       /index.php?pageid=2 [NC,L]
RewriteRule ^domain2\.com/press-kit/$   /index.php?pageid=3 [NC,L]
RewriteRule ^domain2\.com/contact/$     /index.php?pageid=4 [NC,L]
RewriteRule ^domain2\.com/affiliates/$  /index.php?pageid=5 [NC,L]

#blog articles
RewriteRule ^domain2\.com/welcome-to-our-blog/$ /index.php?pageid=b0001 [NC,L]

This avoids the additional RewriteCond directive, however, it requires that you prefix the matched URL-path with the domain in each case. Less chance of error than with the use of the S flag as in the first example above.

In the above example, the hostname remains prefixed to the URL-path at the end of the request for any URL that does not map directly to a file or is not rewritten to /index.php?pageid=<number>. This would then result in a 404.

If you specifically wanted to remove the hostname prefix (perhaps you are using the $_SERVER['REDIRECT_URL'] variable in your error documents, as opposed to $_SERVER['REQUEST_URI']) then you could do this by introducing an additional environment variable (let's call it FINISHED) and remove this prefix at the end.

For example, modify the first rule to check the FINISHED env var (does not exist initially) and introduce a new rule at the end that removes the prefix and sets the env var. Without the FINISHED env var we'll get stuck in a rewrite loop for "unknown URLs" as it will repeatedly prefix and remove the hostname on the URL-path.

# Prefix URL-path with HTTP_HOST if not already prefixed and not FINISHED / No "L" flag
RewriteCond %{ENV:FINISHED} ^$
RewriteCond %{HTTP_HOST}@$1 !^([a-z.-]+)@\1
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) %{HTTP_HOST}/$1

# :
# : Directives for each domain go here (as above)
# :

# Tidy up - remove the HTTP_HOST prefix and set FINISHED env var
RewriteCond %{HTTP_HOST}@$1 ^([a-z.-]+)@\1/(.*)
RewriteRule (.*) %2 [E=FINISHED:1,L]

3. Separate .htaccess config file for each host

Another alternative that is perhaps more suited to this particular situation of separate domains and essentially separate websites (although perhaps a "common" engine) is to have additional (separate) .htaccess files for each site.

Exactly how you configure this could depend on exactly how you've configured your file structure, however, this enables an increased separation between your sites and to even share configs between two or more sites if you wish. You could have resources that relate just to the site being accessed - but you may have something like this already.

For example...

  • You would still have the "master" .htaccess file in the document root (assuming you share a common root directory for all domains).
  • Create a subdirectory for each host. eg. /domain1.com and inside each subdirectory you just have a .htaccess file for that domain.

File structure:

/
    domain1.com/
        .htaccess
    domain2.com/
        .htaccess
    .htaccess
    index.php

/.htaccess

The root .htaccess file would then route the request to the appropriate subdirectory for the requested host.

RewriteEngine On

# If the request is already for index.php in the document root then DONE
RewriteRule ^index\.php$ - [L]

# Rewrite all requests that don't map to files to the appropriate domain subdirectory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) %{HTTP_HOST}/$1 [L]

/domain1.com/.htaccess

Then each domain's .htaccess file contains just the directives for that domain and rewrites the request back to the "common" /index.php file in the document root to render the request.

The rewrite directives take the same "normal" format as used above.

# domain1.com

RewriteEngine On

# Optional - If this subdirectory is accessed directly then redirect back to the root
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule (.*) /$1 [R=301,L]

#main pages
RewriteRule ^members/$     /index.php?pageid=2 [NC,L]
RewriteRule ^contact/$     /index.php?pageid=3 [NC,L]
RewriteRule ^affiliates/$  /index.php?pageid=4 [NC,L]

#blog articles
RewriteRule ^welcome-to-our-blog/$ /index.php?pageid=b0001 [NC,L]

Anyway, food for thought. Some of this comes down to how you structure your application rather than strictly "Grouping RewriteRule by RewriteCond". If you'd like further explanation of any "bit" then just say.

MrWhite
  • 43,179
  • 8
  • 60
  • 84