Apache Rewrite: image directory based on HTTP host - apache

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.

Related

Apache .htaccess redirect to nested folder

I want to redirect www.example.com/folder/ -> www.example.com/folder/subfolder/. I tried the following entries in my .htaccess.
<IfModule mod_rewrite.c>
RewriteEngine On
# 1
# RewriteRule ^/folder/(.*)$ /folder/subfolder/$1 [R=301,NC,L]
# 2
# RewriteRule ^/folder/ https://www.example.com/folder/subfolder/ [R=301,L]
# 3
Redirect 301 /folder/ https://www.example.com/folder/subfolder
</IfModule>
All of the three attempts do not work. Attempt 1 and 2 do not trigger a redirect and attempt 3 results in too many redirects because the pattern /folder/ is inside https://www.example.com/folder/subfolder. This results in https://www.example.com/folder/subfolder/subfolder/subfolder/subfolder....
Are there any solutions available?
This probably is what you are looking for:
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/folder/subfolder
RewriteRule ^/?folder/(.*)$ /folder/subfolder/$1 [R=301,NC,L]
You need to consider that the matching pattern in a RewriteRule that is implemented in a distributed configuration file (".htaccess") is applied to a relative path. You were trying to match an absolute path in your attempt. Which is why the rules never got applied.
That behavior is clearly documented. I recommend that you start looking into the documentation of the tool you are using. As typical for OpenSource software it is of excellent quality and comes with great examples: https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriterule
The modified pattern above considers the leading slash optional (^/?). That way the same rule can get implemented in the http server's central host configuration or inside a distributed configuration file.

Apache2 mod_rewrite difficulty with GET variables

On the website.conf file I have:
<VirtualHost *:80>
DocumentRoot /srv/http/website/cgi-bin
ServerName website
ServerAlias www.website
RewriteEngine on
RewriteRule ^$ ""
RewriteRule ^([a-z]+)$ /?tab=repo
...
My goal is to have http://localhost/ redirect to localhost and http://localhost/word redirect to http://localhost/?tab=word.
With the current directives I get a 404 error, because it's trying to open the file repo # DocumentRoot. All I need is to rewrite the URL to make the word be a GET variable.
A directive like the following works:
RewriteRule /word$ http://localhost/?tab=word
This is obviously somewhat simplistic because I would then have to do it for every possibility.
I experimented with those directives on this website https://htaccess.madewithlove.com/, that I found from another thread on SO, the results are what I expect them to be, I.E.: http://localhost/word is transformed to http://localhost/?tab=word.
Extra info: The website does not have any PHP.
# Virtual Host
RewriteRule ^$ ""
RewriteRule ^([a-z]+)$ /?tab=repo
A directive like the following works:
RewriteRule /word$ http://localhost/?tab=word
The difference with the "working directive" is that you've included a slash prefix. The regex ^([a-z]+)$ does not allow for a slash prefix, so never matches.
You are also failing to use the captured backreference (ie. $1) in the substitution string, so it would always rewrite to /?tab=repo regardless of the URL requested.
Consequently, the first rule, that matches against ^$ will never match either - but this rule is not required. You are not performing a redirect when requesting the root - you just don't want to do anything and instead allow mod_dir to serve the directory index.
In a virtualhost context the URL-path matched by the RewriteRule pattern is a root-relative URL-path, starting with a slash.
So, your rule(s) should be like this instead:
RewriteEngine On
RewriteRule ^/([a-z]+)$ /?tab=$1 [L]
(Or, make the slash prefix optional, ie. ^/?([a-z]+)$)
However, /?tab=<word> is not strictly a valid end-point. What is the actual file that is handling the request? This should be included in the rewrite (and not rely on the DirectoryIndex). You state you are not using PHP, so how are you reading the URL parameter?
I experimented with those directives on this website https://htaccess.madewithlove.com/,
You are not using .htaccess in your example. mod_rewrite behaves slightly differently depending on context (.htaccess, directory, virtualhost and server).
Reference:
https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriterule
What is matched?
In VirtualHost context, The Pattern will initially be matched against
the part of the URL after the hostname and port, and before the query
string (e.g. "/app1/index.html"). This is the (%-decoded) URL-path.
In per-directory context (Directory and .htaccess), the Pattern is
matched against only a partial path, for example a request of
"/app1/index.html" may result in comparison against "app1/index.html"
or "index.html" depending on where the RewriteRule is defined.
After tinkering a bit more I got down to this:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -f
RewriteRule ^(.*)$ %{REQUEST_FILENAME} [PT,L]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d
RewriteRule ^(.*)$ %{REQUEST_FILENAME} [PT,L]
RewriteRule ^/([^index.cgi]{1}.+)$ /index.cgi?tab=$1 [L]
It opens files if they exist and sends requests that don't exist to my C++ cgicc program. The only thing I don't understand is why the -d condition isn't opening directories the same way the -f one opens files.

vhost rewrite rules in apache - get rid of the subdomain without redirecting

I know similar questions have been asked, but for the life of me I can't seem to make it work.
The idea is to rewrite the subdomain as part of the url. We also want the request to be handled by the server without making remote calls, and the url in the client to not be re-written.
To put it simply:
http://test.example.com/(something) should be interpreted as http://example.com/test/(something)
'something' is optional.
I've tried a done of different solutions, but never seem to get it quite right - this is my first experience with that.
# RewriteEngine on
# RewriteCond %{HTTP_HOST} ^test.example.com [NC]
# RewriteRule ^((?!test/).*)$ /test/$1 [L,NC]
# RewriteCond %{HTTP_HOST} ^test\.example\.com$ [NC]
# RewriteRule ^(.*)$ "test%{REQUEST_URI}"
# RewriteRule ^(.*)$ test/$1 [L]
#RewriteEngine on
#RewriteCond %{HTTP_HOST} ^test.example.com [NC]
#RewriteRule ^(.*)$ example.com/test/%{REQUEST_URI} [P,L,NC]
It is important to know that example.com/test/something already exists on a different server and should NOT be used or called.
My thinking was to redirect test.example.com to the new server and then interpret it on the new server as example.com/test by using another vhost (the whole application is being developped without subdomains, but we are going to migrate part by part using subdomains, until we manage to migrate everything and restore the 'normal' way).
In time, we will have sub1.example.com, sub2.example.com, etc. Aliases might play a role here, but I'm out of my depth.
Thanks for the help!
It sounds like you require a "reverse proxy" since example.com is physically hosted on a different server. (However, you state in comments that "in the future" example.com would be back on the same server - at that time you may not need the "reverse proxy" if the subdomain and main domain point to the same filesystem - a simple rewrite may suffice.)
To configure a reverse proxy you need to ensure that mod_proxy and mod_proxy_http (for HTTPS) are enabled and possibly some other (optional) proxy modules depending on requirements.
For what you require, where you are passing the same URL-path from source to target, you can use the ProxyPass and ProxyPassReverse directives in the virtualhost container for the test.example.com subdomain:
ProxyPass / http://example.com/test/
ProxyPassReverse / http://example.com/test/
Alternatively, if you needed a more complex mapping, or if you want/need to do this in .htaccess then you can use mod_rewrite with the P flag (as you are doing above):
RewriteEngine On
RewriteCond %{HTTP_HOST} ^test\.example\.com [NC]
RewriteRule ^ http://example.com/test%{REQUEST_URI} [P]
Importantly, you need the absolute URL (scheme + hostname) in the RewriteRule substitution (missing from your example).
If this host only accepts requests to test.example.com then the preceding condition is not required.
Note that the REQUEST_URI server contains the slash prefix, so this should be omitted in the substitution string. And since you are using the REQUEST_URI server variable and not a backreference then you don't need the capturing group in the RewriteRule pattern. ie. ^(.*)$ can be simplified to just ^. The L is not required when used with P, since L is implied.

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.)

.htaccess require SSL for a particular URL

I want to force Apache to use HTTPS for a particular URL in the following form:
https://www.example.com/signup/*
so
if someone goes to any of the following example URLs directly, Apache will forward the URL over to the HTTPS equivalent site.
e.g.
http://www.example.com/signup --> https://www.example.com/signup
http://www.example.com/signup/basic+plan --> https://www.example.com/signup/basic+plan
http://www.example.com/signup/premium --> https://www.example.com/signup/premium
Anyone know how?
Thanks in advance
Thank Murat,
Yours almost worked but figured out how to get it to exactly work.
The following is what works:
RewriteCond %{SERVER_PORT} 80
RewriteCond %{REQUEST_URI} ^/somefolder/?
RewriteRule ^(.*)$ https://www.domain.com/$1 [R,L]
Notice that I didn't include somefolder in the www.domain.com rewriterule
I think this was what i used:
RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteCond %{REQUEST_URI} ^/somefolder/?
RewriteRule ^(.*)$ https://www.domain.com/somefolder/$1 [R,L]
(from here)
You can use the Redirect directive:
Redirect 301 /signup https://www.example.com/signup
This will automatically preserve anything following /signup in the URL. Be sure to configure this directive only on your non-SSL site, or it might get into a recursive loop!
You should take a look at mod_rewrite documentation
I used the following to require the checkout section of a website to require SSL:
<Directory "/var/www/html">
RewriteEngine on
Options +FollowSymLinks
Order allow,deny
Allow from all
RewriteCond %{SERVER_PORT} !^443$
RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ - [S=1]
RewriteRule ^checkout(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
</Directory>
So for example, hitting http://www.example.com/checkout redirects to https://www.example.com/checkout
The rule will skip file extensions that are typically included within a page so that you don't get mixed content warnings. You should add to this list as necessary.
If you want multiple pages change the RewriteRule to something like:
RewriteRule ^(checkout|login)(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
Of course, the directory should match the actual path on your server. This page may also help with some more information for your specific needs: http://www.whoopis.com/howtos/apache-rewrite.html
I'm using this on a website that runs Plesk 8.6 but that shouldn't matter. This is in my vhost.conf file which is like putting it in your httpd.conf file. I'm not sure if you'd need to adjust anything to use it in a .htaccess file but I doubt it. If adding to a conf file don't forget to restart apache to reload the configuration.
If you are like me and want to use SSL only on particular pages then you also want a rewrite rule that sends you back to regular http for the rest. You can use the following for the reverse effect:
RewriteCond %{SERVER_PORT} ^443$
RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ - [S=1]
RewriteRule !^(checkout|login)(.*)$ http://%{SERVER_NAME}%{REQUEST_URI} [L,R]
If you are using Plesk like I am keep in mind that all non-SSL traffic uses the vhost.conf file but all SSL traffic uses the vhost_ssl.conf file. That means your first rewrite rule to require SSL would go in the vhost.conf file but the second rule to force back to non-SSL will have to go in the vhost_ssl file. If you are using httpd.conf or .htaccess I think you can put them both in the same place.
I've also posted this tutorial on my blog: Apache rewrite rules to force secure/non-secure pages.
You can do this with mod_rewrite -
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^/signup https://example.com/signup
RewriteRule ^/signup/(.*)$ https://example.com/signup/$1
Should work, though I haven't tested it.
-- edit --
Correction, I just tried this on one of my servers, and it works fine for me. You may want to doublecheck your mod_rewrite configuration. Also, if you're using .htaccess, you'll want to make sure overrides are allowed for that directory.
As a side note, this assumes your SSL traffic is coming over port 443. If it isn't, you'll need to adjust the rewrite condition accordingly.
.htaccess files are normally placed in a scope with Options -FollowSymLinks, which blocks Rewrite rules. This is often a security rule.
So a more trivial thing is often needed like this one:
<If "%{HTTPS} != 'on'">
Redirect 301 /your/path https://www.example.com/your/path
</If>
This is a small enhancement to the answer of Greg Hewgill.