.htaccess - Force rule from root .htaccess file despite each subdirectory having own file? - apache

The Problem
I have a piece of code from another Q&A that is a generalized solution to forcing www in web addresses:
# Force www.
RewriteCond %{HTTP_HOST} ^[^.]+\.[^.]+$
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [L,R=301]
My understanding of the logic:
If the HTTP_HOST is defined solely as "(no period string).(no period string)", ie. example.com, then capture the remainder and rebuild the URL with www affixed to the head of the HTTP_POST and captured string.
I have placed this code in a .htaccess file in my web document root. Within that folder are a number of subdirectories which each contain their own .htaccess file to handle redirects and clean urls that are automatically managed by a CMS. All of the entries in these subdirectories tend to follow the following pattern:
RewriteRule ^$ /reason/displayers/generate_page.php?site_id=11111&page_id=222222
RewriteRule ^page$ /subdirectory/page/ [R=permanent]
RewriteRule ^page/$ /reason/displayers/generate_page.php?site_id=111111&page_id=333333
The first problem I have is that the RewriteRule in the solution only triggers for the root page of the website and not any subdirectory or pages:
http(s)://example.com ->http(s)://www.example.com (Good)
http(s)://example.com/subdirectory/ ->http(s)://example.com/subdirectory/ (Bad)
http(s)://example.com/subdirectory/page/ ->http(s)://example.com/subdirectory/page/ (Bad)
Looking at another Q&A, a user suggested adding RewriteOptions inherit to the subdirectory .htaccess. Doing that with my initial RewriteRule in a single test directory results in the following:
http(s)://example.com/subdirectory/page/ ->http(s)://example.com//reason/displayers/generate_page.php/events/?site_id=111111&page_id=333333
The page content does appear, but the URL is certainly less than desirable. The OP of that Q&A left a comment noting that they needed to use %{REQUEST_URI} rather than attempting to capture and reuse it. Using that for the RewriteRule, I get:
http(s)://example.com/subdirectory/page/ ->http(s)://www.example.com/subdirectory/page/?site_id=111111&page_id=333333
Close, but I certainly don't want the parameters to appear there for the sake of SEO.
Questions
Is there any way to apply this kind of rule to all subdirectories and pages without needing to go in and add the RewriteOptions inherit line to each subdirectory .htaccess? Given these are created dynamically by the CMS each time a site is created, manually managing the inclusion of that line isn't ideal.
Assuming that, is there any solution to resolve the "bad" URLs I receive from my attempt to utilize RewriteOptions inherit given what the CMS is placing into the subdirectories automatically?
Alternative
This last one is likely outside of this site's scope, but given it's related, I included it in case the preceding isn't doable or if the following is preferable:
Would it be possible to handle this kind of redirect using VirtualHosts entries? As far as I can tell in my case, both "example.com" and "www.example.com" resolve to the same IP and document root. If they could be separated, www.example.com could remain as is while example.com is set to go somewhere else, allowing for a clear redirect to occur, perhaps?
<VirtualHost 111.11.11.111:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/html/
...
</VirtualHost>

Yes indeed, to apply your rule to all the sub-directories insert this rule in <VirtualHost..> section:
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Related

.htaccess to redirect the root to a subfolder

Using .htaccess I am trying to redirect requests for the root of a site to a subdir. The three approaches I have tried either fail or leave something to be desired.
This fails with indefinite recursion:
Redirect / http://example.org/sub
These don't seem to work for a defaulted index.html:
Redirect / http://example.org/sub/index.html
Redirect /index.html http://example.org/sub/index.html
This works, but leaves the directory at the top level, which necessitates rewriting relative links in the html file:
DirectoryIndex http://example.org/sub/index.html
So, is there any straightforward way just redirect the root totally to a subfolder and have everything work there? I am trying to avoid the fancier features of .htaccess -- I'd probably just prefer the third alternative if it gets too complicated.
In the DocumentRoot of old site have this rule:
RewriteEngine On
RewriteCond %{HTTP_HOST} !example\.org$ [NC]
RewriteRule ^ http://example.org/sub%{REQUEST_URI} [L,NE,R=301]

Apache Rewrite: secondary htaccess for domain specific RedirectMatch

On shared web-hosting my software supports multiple domains (all domains point to the same public_html root directory).
What I want to do is keep redirects (and any RedirectMatch) in their own host specific/dedicated .htaccess file.
Visually the directory structure looks like this...
/public_html/ (all domains are pointed internally to this directory)
/public_html/.htaccess
/public_html/www.example1.com/
/public_html/www.example2.com/
/public_html/www.example3.com/
There are two approaches I'm considering though would appreciate input from others:
The first would be to keep domain specific redirects out of the main .htaccess file as defined above. So I'd like to have redirects handled by the .htaccess files as defined by below if possible...
/public_html/www.example1.com/.htaccess
/public_html/www.example2.com/.htaccess
/public_html/www.example3.com/.htaccess
...if this is not feasible I'll settle for a rewrite to a PHP file to hand off redirects to PHP instead. I imagine this isn't as performance oriented though on the other hand it would give me the opportunity to log redirects and see how long it takes them to level off.
Some clarifications:
I'm using shared web hosting so anything Apache related needs to be done through .htaccess files only.
There are no redirects/matches in the master .htaccess file nor will there ever be since two domains may eventually attempt to use the same redirect.
Since you are on shared host, You cannot afford to have any solutions concerning conf files (which BTW are better). So wont bother to list them. Best way to do the above is like this:
The code was written keeping in mind that none of the domains share any kind of file/data on the server. Every file/data pertaining to a domain is kept under a folder having the name equal to its domainname.
The code below is tested(both static and non static):
RewritEngine on
RewriteBase /
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
And add either of the following to the above:
for doing it statically:
RewriteCond %{HTTP_HOST} ^www\.(example1|example2|example3)(\.com)$ [NC]
RewriteRule ^(.*)$ /www.%1%2/$1 [L]
for doing it statically: and also if you want to access the site without www
RewriteCond %{HTTP_HOST} ^(www\.)?(example1|example2|example3)(\.com)$ [NC]
RewriteRule ^(.*)$ /%1%2%3/$1 [L]
for Non-statically do it: this is a better sol
RewriteRule ^(.*)$ /%{HTTP_HOST}/$1 [L]
All the above will do is redirect URI to their specific domain's folder. All other domain specific rewrites can be handled in the respective folders.
If you have URIs without the www, i.e. example1.com change ^www\.(example1|example2|example3)(\.com)$ to ^(www\.)?(example1|example2|example3)(\.com)$

Apache Rewrite: image directory based on HTTP host

My software supports multiple domain names all pointed at the same directory on the server (a different database for each of course). So these domains...
www.example1.com
www.example2.com
www.example3.com
...all point to...
/public_html/
In the image directory...
/public_html/images/
I have directories that exactly match the host names for each website:
/public_html/images/www.example1.com/
/public_html/images/www.example2.com/
/public_html/images/www.example3.com/
I'm trying to get Apache to rewrite requests so that if you view the image directly and look at the address bar you only see the host name once.
So a request for...
http://www.example1.com/images/book.png
...is fetched by Apache at...
/public_html/images/www.example1.com/book.png
One of the things I've tried and have had success with in different circumstances is the following though it doesn't work in this situation:
RewriteRule ^[^/]*/images(.+) images/%{HTTP_HOST}/$1
Try adding the following to the .htaccess file in the root directory of your site (public_html)
RewriteEngine on
RewriteBase /
#prevent looping from internal redirects
RewriteCond %{ENV:REDIRECT_STATUS} !200
#only rewrite gif, jpg or png
RewriteRule ^(images)(/.+\.(gif|jpg|png))$ $1/%{HTTP_HOST}$2 [L,NC]
Your rule
RewriteRule ^[^/]*/images(.+) images/%{HTTP_HOST}/$1
did not work because you have a leading / before images. In .htaccess the leading / is removed, so the rule would never match.
Here's one of the things I've made for my high performance framework (see my bio).
I give you an advanced RewriteRule, I'm pretty sure you'll have enough material to finish:
Create static domains:
static.example1.com
static.example2.com
static.example3.com
Where all your images will be.
From now on, no more:
www.example1.com/images/www.example1.com/picture.jpg
www.example2.com/images/www.example2.com/picture.jpg
www.example3.com/images/www.example3.com/picture.jpg
but
static.example1.com/picture.jpg
static.example2.com/picture.jpg
static.example3.com/picture.jpg
Nice URLs uh?
Now create a vhost with all your static files:
<VirtualHost *>
ServerName static.example1.com
ServerAlias static.example2.com static.example3.com
</VirtualHost>
Set your document root to the base without the vhost name, so in your case:
DocumentRoot "/public_html/images"
And add this RewriteRule
RewriteCond %{HTTP_HOST} ^static\.([a-zA-Z0-9\-]+)\.com$
# Change the path, and add the request:
RewriteRule (.*) %{DOCUMENT_ROOT}/static.%1.com$1 [QSA,L]
So all in all:
<VirtualHost *>
ServerName static.example1.com
ServerAlias static.example2.com static.example3.com
RewriteCond %{HTTP_HOST} ^static\.([a-zA-Z0-9\-]+)\.com$
# Change the path, and add the request:
RewriteRule (.*) %{DOCUMENT_ROOT}/static.%1.com$1 [QSA,L]
</VirtualHost>
Ok that doesn't aswer exactly to your question so here's the short answer, but I don't like it because it doesn't help you to do a very (very) good job:
RewriteCond %{HTTP_HOST} ^www\.(example1|example2|example3)\.com$
# Change the path:
RewriteRule (.*)(\.(css|js|txt|htc|pdf|jpg|jpeg|gif|png|ico))$ %{DOCUMENT_ROOT}/www.%1.com$1$2 [QSA,L]
And if that's not enough:
Two hints:
If you're not in a hosted environment (= if it's your own server and you can modify the virtual hosts, not only the .htaccess files), try to use the RewriteLog directive: it helps you to track down such problems:
# Trace:
# (!) file gets big quickly, remove in prod environments:
RewriteLog "/web/logs/mywebsite.rewrite.log"
RewriteLogLevel 9
RewriteEngine On
My favorite tool to check for regexp:
http://www.quanetic.com/Regex (don't forget to choose ereg(POSIX) instead of preg(PCRE)!)
John,
I've just posted a separate Q on some of the challenges that you face. I would welcome your comments, but back to your challenge: one trick that you can use an environment variable to store your (preferably validated) host, for example:
RewriteCond %{HTTP_HOST} ^www\.(host1|host2|host3\.com
RewriteRule ^ - [E=HOST:%1]
You might also want to add [S] flags to implement if/then/else logic in your rules. And you can also use the HOST variable in following rule or condition strings (not regexp patterns) as %{ENV:HOST}.
You also need to take a clear look at a full phpinfo() report to understand whether you hosting service is using an mod_php or a mod_suPHP, ... interface and host it supports DNS multihoming. E.g my supplier sets up %{ENV:DOCUMENT_ROOT_REAL} which I need to use instead of %{DOCUMENT_ROOT} when examining file-space.
All of your URI "arrivals" at DOCROOT/ are of the form http://www.exampleX.com/images/book.png so if your .htaccess location is your DOCROOT then your base is /. So assuming the above ENV setting, these should work
RewriteBase /
RewriteCond %{HTTP_HOST} ^www\.(host1|host2|host3)\.com
RewriteRule ^ - [E=HOST:%1]
RewriteCond %{ENV:HOST}==%{REQUEST_URI} !^(.*?)==/image/\1
RewriteRule ^image/(.*) image/%{ENV:HOST}/$1 [L,NS]
The cond is a botch to stop the rewrite rule looping.
Generalised version
The above solution is an already generalised solution as you as for. Just replace the RewriteCond regexp with whatever pattern matches your own naming convention, and I agree that if it is (.*) then you may as well drop the first rule and replace %{ENV:HOST} by %{HTTP_HOST}. You need the RewriteCond guard to prevent the loop which results in a 500.

Apache .htaccess RewriteRule

Here's my situation. I have a web root and several subdirectories, let's say:
/var/www
/var/www/site1
/var/www/site2
Due to certain limitations, I need the ability to keep one single domain and have separate folders like this. This will work fine for me, but many JS and CSS references in both sites point to things like:
"/js/file.js"
"/css/file.css"
Because these files are referenced absolutely, they are looking for the 'js' and 'css' directories in /var/www, which of course does not exist. Is there a way to use RewriteRules to redirect requests for absolutely referenced files to point to the correct subdirectory? I have tried doing things like:
RewriteEngine on
RewriteRule ^/$ /site1
or
RewriteEngine on
RewriteRule ^/js/(.*)$ /site1/js/$1
RewriteRule ^/css/(.*)$ /site1/css/$1
But neither of these work, even redirecting to only one directory, not to mention handling both site1 and site2. Is what I'm trying possible?
EDIT: SOLUTION
I ended up adapting Jon's advice to fit my situation. I have the ability to programatically make changes to my .htaccess file whenever a new subdirectory is added or removed. For each "site" that I want, I have the following section in my .htaccess:
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_URI} !^/index.php$
RewriteCond %{HTTP_COOKIE} sitename=site1
RewriteCond %{REQUEST_URI} !^/site1/
RewriteRule ^(.*)$ /site1/$1 [L]
Index.php is a file that lists all my sites, deletes the "sitename" cookie, and sets a cookie of "sitename=site#" when a particular one is selected. My RewriteConds check,
If the request is not for /
If the request is not for /index.php
If the request contains the cookie "sitename=site1"
If the request does not start with "/site1/"
If all of these conditions are met, then the request is rewritten to prepend "/site1/" before the request. I tried having a single set of Conds/Rules that would match (\w+) instead of "site1" in the third Condition, and then refer to %1 in the fourth Condition and in the Rule, but this did not work. I gave up and settled for this.
If the RewriteRules are in your .htaccess file, you need to remove the leading slashes in your match (apache strips them before sending it to mod_rewrite). Does this work?
RewriteEngine on
RewriteRule ^js/(.*)$ /site1/js/$1
RewriteRule ^css/(.*)$ /site1/css/$1
EDIT: To address the comment:
Yes, that works, but when I do RewriteRule ^(.*)$ /site1/$1, it causes Apache to issue internal server errors. But to me, it seems like that should just be a generic equivalent of the individual rules!
What's happening with that rule is when /something/ gets rewritten to /site/something/, and apache internally redirects, it gets rewritten again, to /site/site/something/, then again, then again, etc.
You'd need to add a condition to that, something like:
RewriteCond %{REQUEST_URI} !^/site/
RewirteRule ^(.*)$ /site/$1 [L]
You need to set up symlinks, which the rewrite rules will use so your absolute links at the server level can follow the symbolic links to the central site hosting account.

htaccess folder rewrite

I've been reading multiple posts on here about htaccess folder rewriting but none seem to fit my question (properly).
My question is:
I have 2 sub folders on the server, website1 and website2.
When a user goes to www.foo.com I wish the visual url to remain the same but want the server URI to go to /website1/ where it will load the index.php for website1
I then want the same thing only when a user goes to www.bar.com again the url does not change but this time it links to /website2/ where it will load the index.php for the 2nd website.
Would really appreciate some help with this as I'm still learning about rewrites. Examples with explanations would be highly appreciated. Also any advice of best practice (if their is any) would also be appreciated.
KingCrunch is right -- the proper way to setup such environment is to use <VirtualHost> directive in Apache config file.
If, for whatever reason this needs to be dona via rewrite and .htaccess .. then you need mod_rewrite to be enabled and .htaccess files to be allowed to contain rewrite rule (AllowOverride directive).
Here are the rules:
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteBase /
# rule #1
RewriteCond %{HTTP_HOST} =www.foo.com
RewriteCond %{REQUEST_URI} !^/website1/
RewriteRule (.*) /website1/$1 [L]
# rule #2
RewriteCond %{HTTP_HOST} =www.bar.com
RewriteCond %{REQUEST_URI} !^/website2/
RewriteRule (.*) /website2/$1 [L]
This code is to be placed in .htaccess file in root folder. If placed elsewhere (e.g. configuration or virtual host context) some tweaking may be required.
Fist rule is for www.foo.com and second for another domain name. These rules are pretty much the same. We tell Apache to check domain name (via {HTTP_HOST} request variable), and if it matches our domain rewrite (internal redirect) URL into one folder deeper. The second condition is to prevent a rewrite loop (to not to rewrite already rewritten URL). It is necessary as Apache, after executing rewrite, goes to the next rewrite iteration (that is how it works), and this condition is required to stop the loop.
Useful link: http://httpd.apache.org/docs/current/rewrite/
I believe that you need to use only RewriteCond and RewriteRule directives. Take a look 'Virtual User Hosts' at http://httpd.apache.org/docs/1.3/misc/rewriteguide.html.
The logical is the same. (I think.)