.htaccess rewrites root to subfolder, yet subfolder app 302 redirects right back to full path - apache

I have a standard .htaccess RewriteRule that silently rewrites any request for webroot into a subfolder which contains a MantisBT installation. So the user types in "example.com" and my server secretly serves them files from "example.com/path/to/mantisbt".
The problem now is that MantisBT's index page immediately does some authentication based logic routing and sends a 302 redirect to the FULL "example.com/path/to/mantis/login", which subverts my rewriting. I'm trying to have everyone access my MantisBT installation as if it resided in the webroot.
Now, I'm aware that after MantisBT's 302 redirect to the full path, I could redirect them AGAIN back to webroot. But redirecting people twice every time MantisBT goes through some routing logic seems like a dirty hack. I also know that I could hack up the MantisBT code, but I hate re-hacking code every time a new version comes out.
So, is there a way to trick MantisBT (or any other app for that matter) into thinking it resides in root, and therefore crafts it's redirect paths based on a webroot-relative url? For example: "example.com/login" instead of "example.com/path/to/mantis/login".
I'd really prefer to resolve this using an Apache .htaccess method, or httpd.conf change. Perhaps DocumentRoot or RewriteBase?

Try adding this rule above the internal rewrite rule that you had before
RewriteCond %{THE_REQUEST} \ /+path/to/mantisbt/([^\?\ ]*)
RewriteRule ^ /%1 [L,R]
this redirects the browser when the browser directly requests anything in /path/to/mantisbt/. Then the rule that you already have to internally rewrite into the mantisbt directory would take effect.

Related

.htaccess redirects if the condition doe not match/ negative condition

I am modifying the .htaccess file of a legacy PHP web application. I am not familiar with apache .htaccess syntax. I found this tutorial. What I am trying to do is that I am trying to redirect all the requests to a URL/ path if the request URL is not a specific URL/ path. For example, all the requests to the website will be redirected to localhost/my-custom-page unless the request URL is localhost/my-custom-page.
I know how to redirect mapping 1 to 1 as follows:
RewriteEngine on
RewriteRule ^my-old-url.html$ /my-new-url.html [R=301,L]
But, what I am trying to do is that redirecting all the requests to the specific page unless the request is to that page. Even the home page will be redirected to that page. How can I do that?
When I tried the following solution
RewriteEngine on
RewriteCond %{REQUEST_URI} !/my-new-url\.html
RewriteRule ^ /my-new-url.html [R=301]
I get the error
I want to check using OR condition as well. For example, if the path is not path-one or path-two, redirect all the requests to path-one.
Your question is a bit vague, due to your wording. But I assume this is what you are actually looking for:
RewriteEngine on
RewriteCond %{REQUEST_URI} !/my-new-url\.html
RewriteRule ^ /my-new-url.html [R=301]
In case you receive an internal server error (http status 500) using the rule above then chances are that you operate a very old version of the apache http server. You will see a definite hint to an unsupported [END] flag in your http servers error log file in that case. You can either try to upgrade or use the older [L] flag, it probably will work the same in this situation, though that depends a bit on your setup.
It is a good idea to start out with a 302 temporary redirection and only change that to a 301 permanent redirection later, once you are certain everything is correctly set up. That prevents caching issues while trying things out...
This rule will work likewise in the http servers host configuration or inside a dynamic configuration file (".htaccess" file). Obviously the rewriting module needs to be loaded inside the http server and enabled in the http host. In case you use a dynamic configuration file you need to take care that it's interpretation is enabled at all in the host configuration and that it is located in the host's DOCUMENT_ROOT folder.
And a general remark: you should always prefer to place such rules in the http servers host configuration instead of using dynamic configuration files (".htaccess"). Those dynamic configuration files add complexity, are often a cause of unexpected behavior, hard to debug and they really slow down the http server. They are only provided as a last option for situations where you do not have access to the real http servers host configuration (read: really cheap service providers) or for applications insisting on writing their own rules (which is an obvious security nightmare).
RewriteCond %{REQUEST_URI} !/my-new-url\.html
RewriteRule ^ /my-new-url.html [R=301]
There are a few potential issues with this, particularly since you hint in a comment that you are perhaps using a front-controller to "route" the URL.
This redirect satisfies the conditions outlined in the question, but does assume that you have no other rewrites, have an essentially "static site" and are not linking to any static resources.
You are missing an L (last) flag, so processing will continue through the file and possibly be rewritten if you have later rewrites.
If you are rewriting the URL to a front-controller in order to route the URL (as you suggest in comments) then this redirect will break, as it will redirect away from the front-controller. You need to only redirect direct requests, ie. when the REDIRECT_STATUS environment variable is empty.
If you are linking to any static resources in the same file space then these will also be redirected. You need to create an exception for any static resources you are using, either by file extension (eg. (css|js|jpg|png)) or by location (eg. /static).
So, try the following instead:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} !\.(js|css|jpg|png)$
RewriteRule !^my-custom-url$ /my-custom-url [R=302,L]
You don't need a separate condition to implement the exception for the URL you are redirecting to. It is more efficient to do this directly in the RewriteRule pattern.
The first condition ensures we are only redirecting direct requests and not rewritten requests to your front-controller.
The second condition avoids any static resources also being redirected. You could alternatively check the filesystem path if all your resources are stored under a common root. Or, as a last resort, implement filesystem checks (ie. RewriteCond %{REQUEST_FILENAME} !-f) if your static resources are too varied - but note that this is less efficient.
You will need to clear your browser cache before testing, since any earlier (erroneous) 301s are cached persistently by the browser.

.htaccess 301 redirect old to new domain, from hosting root to subfolder, working but why?

The scenario: I've moved a WordPress website to a new domain and want to 301 redirect all the pages from the old domain to the new domain. Both sites are on the same hosting account running Apache. The old site is at the root level (public_html), and the new site is in a subfolder (below/inside the root).
I've managed to make this work, but I'd like to learn and understand why it works. So below is a quick overview of my 'journey' and solution, together with three specific questions.
First I tried to do the redirects like this (code added to the root .htaccess file):
# 301 Page Redirects - not working - causes redirect loop
redirect 301 / https://new-domain.com/
redirect 301 /services/ https://new-domain.com/services/
redirect 301 /recipes/ https://new-domain.com/recipes/
But this causes a redirect loop. I'm guessing because the .htaccess file with these rules is at the root level and therefore also affects the subfolders.
Question 1: Is my assumption above about the reason for the redirect loop correct?
Then I tried to be more specific and put this code in the root .htaccess file instead:
# 301 Page Redirects - not working - does nothing at all - not sure why
redirect 301 https://old-domain.com/ https://new-domain.com/
redirect 301 https://old-domain.com/services/ https://new-domain.com/services/
redirect 301 https://old-domain.com/recipes/ https://new-domain.com/recipes/
I was hoping the above code would do the trick, because it's more specific about the old domain. My thinking was that it specifies the old domain exactly and so would circumvent the redirect loop. But instead this code seems to have no effect at all. The redirect loop was gone, but now no redirects were happening anymore at all.
Question 2: Why would the above code not produce any redirects at all?
Then I found this answer and applied the code from that, which works perfectly and creates all the redirects. Plus it's much more elegant than my previous attempts above. This is the code:
# 301 Redirects from old-domain.com to new-domain.com - THIS CODE WORKS - Yay!
RewriteEngine On
RewriteCond %{HTTP_HOST} ^old-domain.com$ [OR]
RewriteCond %{HTTP_HOST} ^www.old-domain.com$
RewriteRule (.*)$ https://new-domain.com/$1 [R=301,L]
Question 3: Why does this code not cause any redirect loops when I place it in the root .htaccess?
I realise I'm copy/pasting code without fully understanding why it works. So I'd love an explanation in simple terms about these behaviours. Thank you.
Answer 1:
From the info that you have provided, I would say no. You did not specify if the new-domain.com website is configured (in apache configuration) with its document root being public_root or public_root/subfolder (judging by the described behaviour I would say it is the former). In that case, when you request https://old-domain.com/anything, the server will (because of the unconditional redirect in your first rule) respond with redirect to https://new-domain.com/anything. Client browser will then request that URL and it will hit the same Apache and same .htaccess, which will again result in the same redirect, causing the loop.
Answer 2:
Redirect syntax:
Redirect [status] [URL-path] URL
The old URL-path is a case-sensitive (%-decoded) path beginning with a
slash.
In your rule, you are specifying [URL-path] as https://old-domain.com/, which is wrong: it can be /, /services/, or /recipes, but not https://old-domain.com/ or https://old-domain.com/services/. The request [URL-path] does not match [URL-path] specified in your rule, so redirect never happens.
Answer 3:
This basically does the same thing as your first rule in Answer 1., with one important difference: the server will respond with redirect only if the hostname in request (or to be more precise, the content of the Host: header in request) is equal to old-domain.com or www.old-domain.com, which will prevent the loop since the second request from the client will use new-domain.com hostname.
Also, from the above, seems like your "new" website in subfolder will never be served: either if old-domain.com or new-domain.com is requested, the site from public_html folder will be shown (and only the hostname in clients browser address bar will change).

Site Redirection with htaccess creates an infinite loop

Currently we have a number of sites hosted in one GoDaddy account. Each site is inside a separate folder and their respective domains are bound to those folders. The problem is that our main site is in the root of the host and our primary domain is linked to this root. The problem with this setup is that if for example, one of my other sites is in a folder called "secondsite", I can reach the website by going to www.secondsite.com (which is fine) but ALSO by going to www.mainsite.com/secondsite, which we absolutely not want.
The idea is to move all the files of the main site to a folder of their own (let's call it "mainsite"). When I talked to GoDaddy they told me to do a 301 redirect to that folder. I have never worked with .htaccess but I looked up how to redirect and found that I needed to write this:
Redirect 301 / http://mainsite.com/mainsite
However if I do that when I try to access the website I get infinite redirection: http://mainsite.com/mainsitemainsitemainsitemainsitemainsitemainsitemainsitemainsitemainsitemainsitemainsitemainsite
I've looked online and tried to use other solutions, like
RedirectPermanent / http://mainsite.com/mainsite
but the effect is the same.
Not sure what I'm doing wrong.
When using Redirect, you're linking path-nodes together, So:
Redirect / /abc/
means, anything starting with / will go to /abc/, e.g.:
/ -> /abc/
/foo -> /abc/foo
/1/2/3/4/5 -> /abc/1/2/3/4/5
And thus, since you're redirecting back to the same host, the / captures everything and you've got an infinite loop.
Try using either RedirectMatch:
RedirectMatch 301 ^/(?!mainsite)(.*)$ /mainsite/$1
or us mod_rewrite:
RewriteCond %{HTTP_HOST} ^(www\.)?mainsite\.com$ [NC]
RewriteRule ^(?!mainsite)(.*)$ /mainsite/$1 [L,R=301]

Apache rewriterule to redirect folder to subfolder without loop

Long story short, I am trying for the last 3 hours to perform what seems to be a basic url redirect. All the website requests to mysite.com/folder should be redirected to mysite.com/folder/subfolder/. The redirect should be performed "only!" if the user enters /folder. If he accesses /folder/file no redirect should happen.
I have tried the following but with no success, all attempts cause an infinite loop:
RewriteRule ^/folder$ /folder/subfolder/ [R]
Redirect 301 /folder /folder/subfolder/
I don't have access to the vhost file, neither am I allowed to perform this using php or a similar 'workaround'. Either way, htaccess should be the best place to do this. Can anybody shade a light on what I am doing wrong?
redirect use a prefix pattern matching, so instead you should use RedirectMatch with a regex, so that you will match only the directory access and not the file access in the subdirectory. You do not need the rewriteRule, which is doing the same thing.
This should work:
RedirectMatch ^/folder/$ /folder/subfolder/
Be careful, you used a 301 Redirect, 301 means permanent, so you may need to close your browser before testing the new rule, your browser has memorized the redirect permanent answer and will not ask it again until you close it.

Why is Apache's RewriteRule revealing local paths?

I'm trying to use RewriteRules in .htaccess with relative paths, but Apache seems to want to output the physical path instead of the server path whenever I try to output a relative path. Absolute and server-root paths work fine. For example:
RewriteEngine On
# this works fine, 127.0.0.1/ab redirects to 127.0.0.1/cd
RewriteRule ^ab$ /cd [R]
# this doesn't work... 127.0.0.1/wx redirects to 127.0.0.1/C:/path/to/files/yz
RewriteRule ^wx$ yz [R]
Adding a "RewriteBase /" solves the problem, but it's tedious to add the path to every .htaccess, and it makes it harder to change the directory structure. Is there a reason RewriteBase defaults to the current physical path instead of the current URI path?
For those who happen to arrive here from Google (like me), the short checklist:
Make sure you have RewriteBase / (or any other value - the statement is what is important)
If you use redirect ([R], [R=30x], etc) - make sure the new URI starts with a / and contains a path relative to your domain root
(If above didn't help yet) Restart Apache, clear your browser's cache (especially if you have used [R=301] at some point)
That's what saved my day, maybe it will save yours too.
It's because of the [R] which means the server will redirect to the new path (so the user's browser will issue a new request with the newly sent uri) instead of translating internally the URI to a local path.
In your first RewriteRule, there is an slash in the new path, thus the server doesn't try to translate it to the local path, but in the second rule, there is no slash, this is why it redirects to a complete local path. This explains too why it works with the RewriteBase set.
Either remove the [R] (you can replace it by a [L] in your case, this avoids the server trying to match other rules once it found a matching one), or add a slash before "yz" in your second RewriteRule.
I'd suggest to simply replace the [R] with a [L]: this way, the user won't see the rewritten path, which is generally what RewriteRules intend to do (mainly for SEO purposes), unless you specifically want to redirect your users to a new URL.
Try this and tell me the result:
# this doesn't work... 127.0.0.1/wx redirects to 127.0.0.1/C:/path/to/files/yz
RewriteRule ^wx$ /yz [R]
put / before yz.