Apache RewriteCond - Works local, not on Host - apache

I have a .htaccess rewrite condition / rule, which works fine on my local, which is OSX mavericks, running apache Apache/2.2.26, but when I deploy to my production server it no longer works, running Apache/2.2.22 (Debian).
this is the condition:
RewriteCond %{REQUEST_URI} ^([a-z]{2})/sitemap.xml$
RewriteRule ^([a-z]{2})/sitemap.xml /sitemaps/$1_sitemap.xml [NC,L]
As you can see, I have a directory for sitemaps in the base directory, and this being a multi-lingual site, I have a sitemap for each language. However I cannot simply create a directory like /en/sitemap.xml for each one, as this effects how the framework deals with the request, as apache trys to serve the directory, rather than passing it to the index file to be handled.
So i create this rewrite condition, which should rewrite the request /en/sitemap.xml to /sitemaps/en_sitemap.xml and as stated, this works great locally, but not on my Debian server, it never matches that regex from the request, it just moves onto the next one and passes the request to the index.php file.. which is wrong!
Any advice / help would be great, I cannot find anything in apache docs referring to mod_rewrite under these 2 versions that may be different.
Thanks

That's because %{REQUEST_URI} value always begins with a leading slash.
Also, your condition is useless since your RewriteRule does the same.
This code should work as expected on both sides
RewriteRule ^([a-z]{2})/sitemap\.xml$ /sitemaps/$1_sitemap.xml [L]

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.

Redirect and Rewrite Rule for Apache

I am trying to install a CalDAV client on my apache webserver, and I am having trouble using a combination of redirects and rewrite rules to get a desired url.
The document root for my webserver is /var/www, and the calendar files are stored in /var/www/agendav/web/public. If I go in through my browser at <website>/agendav/web/public/index.php, I have no trouble getting to the interface and using it, so that is not a problem. However, my desired URL for the calendar is <website>/calendar/, instead of having to go down through the agendav folder tree. I have been trying to perform this with a redirect rule, and a rewrite rule, but to very little success. I have found a few other answers here that have gotten me close, such as this one and this one, with a working redirect, but I am still having issues with the rewrite rule. Here is the current solution I have:
# Rules for the Calendar
Redirect "/calendar" "/agendav/web/public"
RewriteEngine On
RewriteCond %{REQUEST_URI} "^/agendav"
RewriteRule "^/agendav/web/public/(.*?)$" "/calendar/$1"
My current solution seems a little circular. First, I redirect the user to the agendav folder, then then try to hide the redirect behind a rewrite rule, when it seems that I could just get away with a single rewrite rule. Unfortunately the group I am working for is not big enough to have their own dedicated server manager, and I ended up with the job despite knowing very little about it. Any help to get this would be greatly appreciated.
You don't need the redirect, you can do it with just a rewrite rule, the problem was that you have the condition and the rule reversed, you were using the real path for the conditions instead of the "virtual" path:
RewriteEngine on
RewriteCond %{REQUEST_URI} "^/calendar" [NC]
RewriteRule "^calendar/(.*)" "/agendav/web/public/$1"
With this rules all requests for http://yourwebsite/calendar/* are internally served with http://yourwebsite/agendav/web/public/*.

Apache 2.4 .htaccess friendly URL rewrite

Having some issues understanding the .htaccess file and getting it to work properly. .htaccess is recognized....i entered plain test at the top and got the internal server error. And it appears mod-rewrite is working...because I downloaded a test php file and it works.
I am using Apache 2.4.23 with no php pages
However i cant seem to get this to work right.
I am trying to take this url
http://example.com/ProjectTest/index.shtml?dynContent=Content1
and rewrite it like this
http://example.com/ProjectTest/Content1
I am also trying to do this generically across the site since ?dynContent=whatever will be a constant.....and trying to keep the same path.....like the below example
Change this
http://example.com/ProjectTest/ProjectFolder/index.shtml?dynContent=Content1
to this
http://example.com/ProjectTest/ProjectFolder/Content1
I am not very good with the .htaccess files
UPDATE: Ok...I got this to work.....not really sure why it works....but when I type in the test-dev/ProjectTest/apples or test-dev/ProjectTest/oranges it calls up the appropriate content.
This is what I used
RewriteRule ^ProjectTest/([^/\.]+)/?$ ProjectTest/index.shtml?dynContent=$1 [L]
and then I rewrote it to be this and now it works for any directory
RewriteRule ^(.*)/([^/\.]+)/?$ $1/index.shtml?dynContent=$2 [L]
Try this:
RewriteEngine On
RewriteCond %{QUERY_STRING} ^dynContent=(.+)$
RewriteRule ^/(.+)+/index.shtml $1/%1 [PT]
This requires mod_rewrite to be activated. The value of the bracket in dynContent=(.+) is stored in %1 and the bracket's value in ^/(.+)+/index.shtml is stored in $1. Both together creates your target path http://example.com/ProjectTest/ProjectFolder/Content1

Apache %{REQUEST_URI} not working correctly

I am not using Virtual Hosts or anything fancy though I have some .htaccess files setup. Following is my rewrite rule in httpd.conf:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/app/smsapi [NC]
RewriteRule (.*) https://www.example.com/uri=%{REQUEST_URI} [R,L]
This rule basically says that if the uri does not begin with /app/smsapi then fire the rewrite. But when I restart the server and try it I get some weird results.
When I request the URL https://www.example.com/app/smsapi/index.php, I get a 200 Success code which is as expected. But, when I request the URL http://www.example.com/app/smsapi/index.php, it redirects to https://www.example.com/uri=/app/smsapi/index.php. So it actually fires the rule even though the request URI does not satisfy the condition.
So, then I decided to turn off the rewrite rules and give it a go. Now, both those URL give me a 200 Success code.
Now, I know this problem cannot be solved easily by other people who do not have access to the server, but am I right in saying that this is certainly a problem with REQUEST_URI not firing correctly? I have shown that without the rewrite rule, everything works normally, but with the rewrite rule, the second URL is redirected. Therefore, the redirection must be caused by the rewrite rule? Additionally, the condition for redirect rule is not satisfied. Doesn't this prove that there is something wrong with the functioning of the rewrite rule?
Is there any other possibility?
UPDATE
Something very weird is happening here. I setup a local server and tried the same rule and what I got for the URL http://192.168.0.112/app/ is
http://192.168.0.112/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/uri=/app/
which is correct because as long as the URL is not like /app/smsapi, it should redirect it. Wonder why this is not happening on the real server. Also, where you insert these rules seems to make a difference. (I am only including these rules after the LoadModule command).
On localhost, if I put these rules either above or below the Directory section, it won't work. But, if I include it inside the Directory section it will.
On server, if I include the rules inside the Directory section, they won't work. But, if I include them either above or below the Directory section, they start working.
This seems to me to be due to a difference in the versions. My localhost is an Ubuntu Desktop 16.04 running Apache 2.4.18. While the server is CentOS 6.8 running Apache 2.2.15.
But, i think the mystery as to why on the server redirect happens only once (though it is configured to go upto 20 times) has something to do with https. Which is also related to the original problem in which https is redirected even on a non-matching rule.
Clues anyone?
UPDATE
I updated the httpd.conf file with the same rules but I used http:// instead of https:// and it gave me the correct result with 20 redirects. That means I have isolated the problem to https.
You are reporting the exact issue in the first phrase: "I am not using Virtual Hosts or anything fancy though I have some .htaccess files setup"
.htaccess is "fancy" and overcomplicated, not virtualhosts.
If you had defined that RewriteCond in virtualhost in the first place it would work, but .htaccess is per-dir context (aka a nightmare) and the regex ^/ will never match in that context.
If you want to match REQUEST_URI in per-dir context (directory or .htaccess) you need to drop the initial slash, that is:
RewriteCond %{REQUEST_URI} !^app/smsapi [NC]
Extra, also consider you MAY NOT need to add a RewriteCond for this:
RewriteRule ^(?!app/smsapi)(.*) https://www.example.com/uri=$1 [R,L]

Does REQUEST_URI hide or ignore some filenames in .htaccess?

I'm having some difficulty with a super simple htaccess redirect.
All I want to do is rewrite absolutely everything, except a couple files.
htaccess looks like this:
RewriteEngine On
RewriteCond %{REQUEST_URI} !sitemap
RewriteCond %{REQUEST_URI} !robots
RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]
The part that works is that everything gets redirected to new domain as it should be. And I can also access robots.txt without being forwarded, but not with sitemap.xml. If I try to go to sitemap.xml, the domain forwards along anyway and opens the sitemap file on the new domain.
I have this exact same issue when trying to "ignore" index.html. I can ignore robots, I can ignore alternate html or php files, but if I want to ignore index.html, the regex fails.
Since I can't actually SEE what is in the REQUEST_URI variable, my guess is that somehow index.html and sitemap.xml are some kind of "special" files that don't end up in REQUEST_URI? I know this because of a stupid test. If I choose to ignore index.html like this:
RewriteCond %{REQUEST_URI} !index.html
Then if I type example.com/index.html I will be forwarded. But if I just type example.com/ the ignore actually works and it shows the content of index.html without forwarding!
How is it that when I choose to ignore the regex "index.html", it only works when "index.html" is not actually typed in the address bar!?!
And it gets even weirder! Should I type something like example.com/index.html?option=value, then the ignore rule works and I do NOT get forwarded when there are attributes like this. But index.html by itself doesn't work, and then just having the slash root, the rule works again.
I'm completely confused! Why does it seem like REQUEST_URI is not able to see some filenames like index.html and sitemap.xml? I've been Googling for 2 days and not only can I not find out if this is true, but I can't seem to find any websites which actually give examples of what these htaccess server variables actually contain!
Thanks!
my guess is that somehow index.html and sitemap.xml are some kind of "special" files that don't end up in REQUEST_URI?
This is not true. There is no such special treatment of any requested URL. The REQUEST_URI server variable contains the URL-path (only) of the request. This notably excludes the scheme + hostname and any query string (which are available in their own variables).
However, if there are any other mod_rewrite directives that precede this (including the server config) that rewrite the URL then the REQUEST_URI server variable is also updated to reflect the rewritten URL.
index.html (Directory Index)
index.html is possibly a special case. Although, if you are explicitly requesting index.html as part of the URL itself (as you appear to be doing) then this does not apply.
If, on the other hand, you are requesting a directory, eg. http://example.com/subdir/ and relying on mod_dir issuing an internal subrequest for the directory index (ie. index.html), then the REQUEST_URI variable may or may not contain index.html - depending on the version of Apache (2.2 vs 2.4) you are on. On Apache 2.2 mod_dir executes first, so you would need to check for /subdir/index.html. However, on Apache 2.4, mod_rewrite executes first, so you simply check for the requested URL: /subdir/. It's safer to check for both, particularly if you have other rewrites and there is possibility of a second pass through the rewrite engine.
Caching problems
However, the most probable cause in this scenario is simply a caching issue. If the 301 redirect has previously been in place without these exceptions then it's possible these redirections have been cached by the browser. 301 (permanent) redirects are cached persistently by the browser and can cause issues with testing (as well as your users that also have these redirects cached - there is little you can do about that unfortunately).
RewriteCond %{REQUEST_URI} !(sitemap|index|alternate|alt) [NC]
RewriteRule .* alternate.html [R,L]
The example you presented in comments further suggests a caching issue, since you are now getting different results for sitemap than those posted in your question. (It appears to be working as intended in your second example).
Examining Apache server variables
#zzzaaabbb mentioned one method to examine the value of the Apache server variable. (Note that the Apache server variable REQUEST_URI is different to the PHP variable of the same name.) You can also assign the value of an Apache server variable to an environment variable, which is then readable in your application code.
For example:
RewriteRule ^ - [E=APACHE_REQUEST_URI:%{REQUEST_URI}]
You can then examine the value of the APACHE_REQUEST_URI environment variable in your server-side code. Note that if you have any other rewrites that result in the rewritting process to start over then you could get multiple env vars, each prefixed with REDIRECT_.
With the index.html problem, you probably just need to escape the dot (index\.html). You are in the regex pattern-matching area on the right-hand side of RewriteCond. With the un-escaped dot in there, there would need to be a character at that spot in the request, to match, and there isn't, so you're not matching and are getting the unwanted forward.
For the sitemap not matching problem, you could check to see what REQUEST_URI actually contains, by just creating an empty dummy file (to avoid 404 throwing) and then do a redirect at top of .htaccess. Then, in browser URL, type in anything you want to see the REQUEST_URI for -- it will show in address bar.
RewriteCond %{QUERY_STRING} ^$
RewriteRule ^ /test.php?var=%{REQUEST_URI} [NE,R,L]
Credit MrWhite with that easy test method.
Hopefully that will show that sitemap in URL ends up as something else, so will at least partially explain why it's not pattern-matching and preventing redirect, when it should be pattern-matching and preventing redirect.
I would also test by being sure that the server isn't stepping in front of things with custom 301 directive that for whatever reason makes sitemap behave unexpectedly. Put this at the top of your .htaccess for that test.
ErrorDocument 301 default