Looking for a RewriteBase equivalent using RewriteRule - apache

I'm facing an Apache configuration issue which can be summarized like follows.
On a unique hosting system I have a lot of different test sites, each one in its own subdirectory, so they are accessible through an url like myhostname.fr/sitename.
Hence in the corresponding .htaccess, the common practice is to have a RewriteBase /sitename before any of the RewriteCond+RewriteRule sets, and it works fine.
Now for one of these sites (say in the specialsite subdirectory) I had to create a dedicated domain so the url looks like domainname.myhostname.fr.
Then for this site to work the .htaccess now needs RewriteBase / instead of RewriteBase /specialsite, and it works fine too.
Here is the trick: being not so familiar with Apache I decided to experiment and wanted to also keep allowed to access this site through the common url myhostname.fr/specialsite.
So I had to find a way to conditionally use one of the above RewriteBase, depending on which is the current url.
The first way I tried was to work like this:
<If "%(HTTP_HOST) =~ domainname\.myhostname\.fr">
RewriteBase /
</If>
<If "%(HTTP_HOST) =~ myhostname\.fr/specialsite">
RewriteBase /specialsite
</If>
But I got a HTTP 500 error, and I take much time to understand that the <If> directive is available as of Apache 2.4, while my hosting only offers Apache 1.3!
So (thanks to some other SO answers) I thinked to another way, which is to first do:
RewriteCond %{HTTP_HOST} domainname\.myhostname\.fr
RewriteRule ^ - [E=VirtualRewriteBase:/]
RewriteCond %{HTTP_HOST} myhostname\.fr/specialsite
RewriteRule ^ - [E=VirtualRewriteBase:/specialsite/]
Then prepend all further RewriteRule replacement with the given VirtualRewriteBase, like in this one:
RewriteRule ^ %{ENV:VirtualRewriteBase}index.php [L]
But while it works fine for the domain-access version, it gives me an HTTP 404 error for the subdirectory-access version.
So in order to watch at how the replacement applied I changed the above rule for:
RewriteRule ^ %{ENV:VirtualRewriteBase}index.php [R,L]
And I observed that the redirected url looked like this:
http://myhostname.fr/kunden/homepages/7/d265580839/htdocs/specialsite/index.php
where kunden/homepages/7/d265580839/htdocs/ is the full document-root of my hosting.
You can notice that the document-root has been inserted between the two parts of the original url.
Moreover, the result is exactly the same whatever I put in place of /specialsite/ in my VirtualRewriteBase!
So here is my main question: why and how does this happen?
Also I'm obviously interested to a possible alternative solution to achieve the double-access availibility.
But above all I would like to understand...

But while it works fine for the domain-access version, it gives me an
HTTP 404 error for the subdirectory-access version.
That's because your second condition is never matched. Indeed, HTTP_HOST only contains the... http host ! The /specialsite is part of the REQUEST_URI (or can also be matched in RewriteRule directly).
This code should work (anyway, i don't know if it would solve totally your problem, but that's a first step)
RewriteCond %{HTTP_HOST} ^domainname\.myhostname\.fr$ [NC]
RewriteRule ^ - [E=VirtualRewriteBase:/]
RewriteCond %{HTTP_HOST} ^myhostname\.fr$ [NC]
RewriteCond %{REQUEST_URI} ^/specialsite(?:/|$) [NC]
RewriteRule ^ - [E=VirtualRewriteBase:/specialsite/]

Related

Editing .htaccess file to modify URL

I'm trying to modify my .htaccess file to modify my URL and have tried many methods but cannot achieve exactly what I want. For example I have this URL:
http://mywebsite.com/FOLDER/index.php?id=5
Now I want it to look like:
http://mywebsite.com/FOLDER/5
or
http://mywebsite.com/FOLDER/ID/5
My .htaccess contains the following code:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^index/([0-9]+)/([0-9a-zA-Z_-]+) index.php?id=$1 [NC]
I cannot figure out what's wrong. Thanks.
You can use:
RewriteEngine on
# external redirect from actual URL to pretty one
RewriteCond %{THE_REQUEST} \s/+FOLDER/index\.php\?id=(\d+) [NC]
RewriteRule ^ /FOLDER/%1? [R=301,L,NE]
# internal forward from pretty URL to actual one
RewriteRule ^FOLDER/(\d+)/?$ FOLDER/index.php?id=$1 [L,QSA,NC]
The first argument of RewriteRule is what the incoming url without domain and without preceding paths (more on that later) is going to be matched against. This url is, in your case, http://mywebsite.com/FOLDER/5. Assuming that your .htaccess file is in your DocumentRoot, the regex will match against FOLDER/5.
You are currently trying to match FOLDER/5 with ^index/([0-9]+)/([0-9a-zA-Z_-]+), which is not going to work. A better regex would be ^(.*)/([0-9]+)$ or ^(.*)/ID/([0-9]+)$. You can then rewrite to $1/index.php?id=$2. I would recommend using the [L] flag to stop rewriting for this round to avoid common problems with multiple rules matching while you do not expect them to.
Besides this, make sure that your .htaccess files are being read (e.g. by checking that if you enter garbage, you get a 500 internal server error), that mod_rewrite is enabled, that you are allowed to override FileInfo. You also may need to turn AcceptPathInfo off.

Apache mod_rewrite for specific folders and paths

I have found dozens of articles online on how to setup mod_rewrites but for the love of God I can't figure out how to PROPERLY force HTTPS on ALL pages and after that force HTTP on certain directories or (already rewritten) pages.
Now this one gets really tricky as I need HTTPS on this directory, except for two cases, such as "/surf" which actually is rewritten from "surf.php", and "promote-([0-9a-zA-Z-]+)$" which is rewritten from "promote.php?user=$1" :
<Directory /home/rotate/public_html/ptp/>
AllowOverride None
Order Deny,Allow
Allow from all
Options +SymLinksIfOwnerMatch
ErrorDocument 404 "<h1>Oops! Couldn't find that page.</h1>"
RewriteEngine On
RewriteRule ^promote-([0-9]+)$ promote.php?user=$1 [QSA,NC,L]
RewriteRule ^([^.?]+)$ %{REQUEST_URI}.php [L]
</Directory>
I have tried some stuff but which only resulted in some weird redirection loops...
RewriteCond %{HTTPS} on
RewriteRule !^(surf|promote-([0-9]+)$) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
So basically I need to force HTTPS everywhere in /ptp/ except /ptp/surf (which is rewritten from surf.php AND /ptp/promote-123 which is rewritten from promote.php?user=123
Currently I'm using PHP to redirect to HTTP or HTTPS as per my needs but I know that it would be much faster if I could manage to do it via rewrites.
Any pointers, tips, suggestions? Thanks.
UPDATE2: This worked:
RewriteCond %{HTTPS} off
RewriteRule !^(surf|promote(-[0-9]+)?) https://%{HTTP_HOST}%{REQUEST_URI} [R=301]
RewriteRule ^promote-([0-9]+)$ promote.php?user=$1 [NC,L]
RewriteRule ^([^.?]+)$ %{REQUEST_URI}.php
However, the resources such as javascript, fonts etc, were being blocked by the browser, unless I specified absolute HTTPS paths. Note that this never happened when redirecting through PHP...
I changed a little bit and it works perfectly
Changes
Remove the Change the RewriteRule to match file to .php to bottom.
Remove the $ sign that is End of the pattern
As Said in the update promote-1111 will redirect to promote.php?user=$1 change the promote-[0-9]+ to promote(-[0-9]+)? otherwise it will override in the second redirection as you redirecting it to promote.php?user=$1
The Code
RewriteCond %{HTTPS} off
RewriteRule !^(surf|promote(-[0-9]+)?) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteRule ^([^.?]+)$ %{REQUEST_URI}.php [L]
The page surf
The Page Index
Never mind the error message shown in this image. Since I tried it from my localhost, it won't have a certificate.
Will work with servers
Your rules aren't in the "update" working because of side effects of using <Directory> context. Each substitution starts processing again.
When you request /promote-123 and rewrite it to put the numbers in the query string, you can't then match the numbers as if they're still in the path. You'll need to match the rewriten path and the numbers with RewriteCond %{QUERY_STRING} (if you care about the numbers)

Redirect loop with simple htaccess rule

I have been pulling my air out over this. It worked before the server migration!
Ok so basically it's as simple as this:
I have a .php file that I want to view the content of using a SEO friendly URL via a ReWrite rule.
Also to canonicalise and to prevent duplicate content I want to 301 the .php version to the SEO friendly version.
This is what I used and has always worked till now on the new server:
RewriteRule ^friendly-url/$ friendly-url.php [L,NC]
RewriteRule ^friendly-url.php$ /friendly-url/$1 [R=301,L]
However disaster has struck and now it causes a redirect loop.
Logically I can only assume that in this version of Apache it is tripping up as it's seeing that the script being run is the .php version and so it tries the redirect again.
How can I re-work this to make it work? Or is there a config I need to switch in WHM?
Thanks!!
This is how your .htaccess should look like:
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteBase /
# To externally redirect /friendly-url.php to /friendly-url/
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s/+(friendly-url)\.php [NC]
RewriteRule ^ /%1/? [R=302,L]
## To internally redirect /anything/ to /anything.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1\.php -f
RewriteRule ^(.+?)/$ $1.php [L]
Note how I am using R=302, because I don't want the rule to cache on my browser until I confirm its working as expected, then, once I can confirm its working as expected I switch from R=302 to R=301.
Keep in mind you may have also been cached from previous attempts since you're using R=301, so you better of trying to access it from a different browser you have used just to make sure its working.
However disaster has struck and now it causes a redirect loop.
It causes a redirect loop because your redirecting it to itself, the different on my code is that I capture the request, and redirect the php files from there to make it friendly and then use the internal redirect.
The exact same .htaccess file will work differently depending on where it's placed because the [L]ast flag means something different depending on location. In ...conf, [L]ast means all finished processing so get out, but in .htaccess the exact same [L]ast flag means start all over at the top of this file.
To work as expected when moving a block of code from ...conf to .htaccess, most .htaccess files will need one or the other of these tweaks:
Change the [L]ast flags to [END]. (Problem is, the [END] flag is only available in newer [version 2.3.9 and later] Apaches, and won't even "fall back" in earlier versions.)
Add boilerplate code like this at the top of each of your .htaccess files:
*
RewriteCond %{ENV:REDIRECT_STATUS} !^[\s/]*$
RewriteRule ^ - [L]

How to add "everything else" rule to mod_rewrite

How can I make mod_rewrite redirect to a certain page or probably just throw 404 if no other rules have been satisfied? Here's what I have in my .htaccess file:
RewriteEngine on
RewriteRule ^\. / [F,QSA,L]
RewriteRule ^3rdparty(/.*)$ / [F,QSA,L]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^((images|upload)/.+|style.css)$ $1 [L]
RewriteRule ^$ special [QSA]
RewriteRule ^(special|ready|building|feedback)/?$ $1.php [QSA,L]
RewriteRule ^(ready|building)/(\d+)/?$ show_property.php?type=$1&property_id=$2 [QSA,L]
RewriteRule . error.php?code=404 [QSA,L]
This is supposed, among other things, to send user to error.php if he tries to access anything that was not explicitly specified here (by the way, what is the proper way to throw 404?). However, instead it sends user from every page to error.php. If I remove the last rule, everything else works.
What am I doing wrong?
What is happening is that when you are doing a rewrite, you then send the user to the new URL, where these rewrite rules are then evaluated again. Eventually no other redirectoin rules will be triggered and it will get to the final rule and always redirect to the error.php page.
So you need to put some rewrite conditions in place to make this not happen.
The rewrite engine loops, so you need to pasthrough successful rewrites before finally rewriting to error.php. Maybe something like:
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_URI} !^/(special|ready|building|feedback|show_property)\.php
RewriteCond %{REQUEST_URI} !^/((images|upload)/.+|style.css)$
RewriteRule ^ error.php?code=404 [QSA,L,R=404]
Each condition makes sure the URI isn't one of the ones your other rules have rewritten to.
The R=404 will redirect to the error.php page as a "404 Not Found".
Unfortunatelly, it didn't work - it allows access to all files on the server (presumably because all conditions need to be satisfied). I tried an alternate solution:
Something else must be slipping through, eventhough when I tested your rules plus these at the end in a blank htaccess file, it seems to work. Something else you can try which is a little less nice but since you don't actually redirect the browser anywhere, it would be hidden from clients.
You have a QSA flag at the end of all your rules, you could add a unique param to the query string after you've applied a rule, then just check against that. Example:
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^((images|upload)/.+|style.css)$ $1?_ok [L,QSA]
then at the end:
RewriteCond %{QUERY_STRING} !_ok
RewriteRule ^ error.php?code=404&_ok [QSA,L,R=404]
In theory if none of the rules are matched (and the requested URL does not exist), it's already a 404. So I think the simplest solution is to use an ErrorDocument, then rewrite it:
RewriteEngine On
ErrorDocument 404 /404.php
RewriteRule ^404.php$ error.php?code=404 [L]
# All your other rules here...
You can do the same for any other HTTP error code.
The problem here is that after the mod_rewrite finishes rewriting the URL, it is resubmitted to the mod_rewrite for another pass. So, the [L] flag only makes the rule last for the current pass. As much better explained in this question, mod_rewrite starting from Apache version 2.3.9, now supports another flag - [END], that makes the current mod_rewrite pass the last one. For Apache 2.2 a number of solutions are offered, but since one of them was a bit clumsy and another didn't work, my current solution is to add another two rules that allow a specific set of files to be accessed while sending 404 for everything else:
RewriteRule ^((images|upload)/.+|style.css|(special|ready|building|feedback|property).php)$ - [QSA,L]
RewriteRule .* - [QSA,L,R=404]
I think your last rule should be
RewriteRule ^(.*)$ error.php?code=404&query=$1 [QSA,L]
You could leave out the parenthesis and the $1 parameter, but maybe it's useful to know, what the user tried to achieve.
Hope, this does the trick!

How do I get apache RewriteRule working correctly for a subdomain?

I just setup a subdomain with the following RewriteCond:
RewriteCond $1 !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?([^/]+)$ search.php?q=$1 [L,NS]
I'm using the same rewrite condition on my main domain and it works perfectly. However, when I set it up on the subdomain, it simply outputs "index.php" when going to http://sub.domain.com
Every page on the subdomain outputs the page name in the body instead of processing the code, except for the search page, which appears to be working correctly.
What can I do to correct this issue?
I haven't played with your exact regex with mod_rewrite, but if I was looking at writing that regex in another engine, I would have to escape the slash. Also, given that $ is used to indicate a back reference, would that need escaping too (would your $ symbols in the regex be necessary as there is likely to be more text in the URI and it is not matched at the end of a string)?
I would try
RewriteCond $1 !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?([^\/]+)$ search.php?q=$1 [L,NS]
One other thing. Normally $ at the end of a regex means "only match if this is the end of the string". So from that, if RewriteCond is matching on ^search.php$ but the URL is search.php?q=... then I would think that this wouldn't match because search.php is not the end of the string. So that would look like the following (assuming you don't need to change anything else from your original).
RewriteCond $1 !^search.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?([^/]+)$ search.php?q=$1 [L,NS]
In the main config the path always begins with / and you need an absolute path:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/([^/]+)$ %{DOCUMENT_ROOT}/search.php?q=$1 [L]
In an .htaccess you need a RewriteBase which is stripped from the url (no / in the Rule now) and the path is relative.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]+)$ search.php?q=$1 [L]
Several things come to mind here:
I have a few suggestions/comments/gotchas. Hopefully one of them is useful to you:
Make sure search.php isn't just echoing out its $_GET parameters. While this sounds obvious in retrospect, it's one of the more overlooked solutions.
RewriteRule works slightly differently when you specify it in a server configuration file than if you specify it in an .htaccess. Specifically, ^/ is wrong in a server config version as the entire URL is used (http://sub.domain.com/blah).
Make sure no other rewrite rules are being processed for this subdomain first, either in the main httpd.conf / apache2.conf or .htaccess.
Make sure RewriteEngine On appears in your configuration, as it is activated per-VirtualHost.
The NS flag will ignore redirects done using a relative Redirect or relative RewriteRule.
It sounds like the pattern '^/?([^/]+)$' may not be matching at all.
I'd activate RewriteLog, crank RewriteLogLevel to level 3 or above, and see if your pattern is matching at all. If not, start with a simpler pattern, and then work your way to a more complex pattern.
Or, something else is matching the pattern, so the request never gets to 'RewriteRule ^/?([^/]+)$' at all. You will see this in the RewriteLog.
I believe I recently had a problem where '^/' didn't match in certain cases on a Virtual Host. But '/' worked. The folks in the #httpd on Freenode.org helped me. If I can find this in my notes, I'll post it here.