Rewrite rules gives inifinite loop (error 500) despite [L] option - .htaccess

I have to types of urls. The first one contains number and the second does not, e.g.:
/forms/my-forms/form/123/edit
/forms/my-forms/
I want to take out all numbers from path (if they have no letters before/after the / sings so e.g. 123 but not a123) to $_GET variable and the rest of the path to another $_GET so I will have something like that after rewriting:
index.php?path=forms/my-forms/form/edit&id=123
index.php?path=forms/my-forms/
I've created the .htaccess for this:
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteBase /
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond $1 !^(index\.php|robots\.txt)
RewriteRule ^(.*)/([0-9]+)(.*)?$ ./index.php?path=$1$3&id=$2 [L]
RewriteRule ^(.*)$ ./index.php?path=$1 [L]
But it gives me infinitive loop (500 Interla Server Error), despite using [L] for both rules.
When I remove one of the rules, the other works fine (but of course I only have processing of first or second type of urls).

L flag doesn't do what you think it does. L just exists the current Rewrite Cycle and sends the uri for next fase of processing. You are getting rewrite loop error because of your last rule
RewriteRule ^(.*)$ ./index.php?url=$1 [L]
This rewrites everything to /index.php , for your example
in first iteration
http://example.com/foobar gets rewritten to /index.php?url=foobar and then , On the second rewrite iteration /index.php?url=foobar gets rewritten to itself /index.php?url= and thus apache return a 500 Error status to client.
To fix the loop error, you need to tell mod-rewrite to exit the rule processing in first iteration, You can Replace L with END if you are on apache 2.4 , if you are on lower version of apache you can use the following condition above the rule that is causing internal loops
RewriteCond %{ENV_REDIRECT_STATUS} !200

Try with below rule, I didn't tried for now but .* is the one causing problem It matches everything in url and after that most of the rule cease to work other than with .*.
RewriteRule ^(.+?)/([0-9]+)(.+?)?$ ./index.php?path=$1$3&id=$2 [L]
RewriteRule ^(.+?)$ ./index.php?path=$1 [L]

Related

Why is there a error 500 on a Subdomain to folder

This is what's inside the .htaccess file:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)\.domain\.com
RewriteRule ^(.*)$ Subdomains/%1/$1 [L,NC,QSA]
When I'm on the domain (eg: x.example.com), it displays error 500. What am I doing wrong?
Example:
x.example.com -> show example.com/Subdomains/x but keep the URL of x.example.com
y.example.com -> show example.com/Subdomains/y but keep the URL of y.example.com
z.example.com -> show example.com/Subdomains/z but keep the URL of z.example.com
RewriteCond %{HTTP_HOST} ^(.*)\.example\.com
RewriteRule ^(.*)$ Subdomains/%1/$1 [L,NC,QSA]
Without any other directives to prevent it, the above will result in an internal rewrite loop, which will result in a 500 Internal Server Error response to the browser.
For example, if you request https://sub.example.com/foo then...
Request is rewritten to /Subdomains/sub/foo.
In a directory context (ie. htaccess) the rewriting process starts over, passing /Subdomains/sub/foo as in the input to the next round of processing...
Request is rewritten to /Subdomains/sub/Subdomains/sub/foo.
The rewriting process starts over...
Request is rewritten to /Subdomains/sub/Subdomains/sub/Subdomains/sub/foo.
The rewriting process starts over...
etc. until the server gives up. (Default is after 10 internal rewrites.)
The L flag does not stop all processing in a directory context (ie. htaccess). It simply stops the current round of processing. The rewrite process continues until the URL passes through unchanged.
The quick fix on Apache 2.4 is to simply replace the L flag with END. This causes all processing to stop. No further passes through the rewrite engine occur.
For example:
RewriteCond %{HTTP_HOST} ^(.*)\.example\.com
RewriteRule (.*) Subdomains/%1/$1 [END]
The NC and QSA flags are superfluous here.
Note that the regex ^(.*)\.example\.com matches any number of sub-subdomains, including www itself, if that is a concern?
Without using the END flag (Apache 2.4) then you would need to explicitly check that you have not already rewritten the URL. One way is to prevent any further rewrites if /Subdomains/.... has already been requested:
RewriteCond %{HTTP_HOST} ^(.*)\.example\.com
RewriteRule ^((?!Subdomains/).*)$ Subdomains/%1/$1 [L]
Note that if you have another .htaccess file located in the /Subdomains/<sub> subdirectory that also uses mod_rewrite then this will have also prevented a rewrite loop since mod_rewrite directives are not inherited by default.
UPDATE: Is there a way to check if the folder dosent exist then just redirect to domain.com?
Yes, you can do this, for example:
# If subdomain does not map to a subdirectory then redirect to root of main domain
RewriteCond %{HTTP_HOST} ^(.*)\.(example\.com)
RewriteCond %{DOCUMENT_ROOT}/Subdomains/%1 !-d
RewriteRule ^ https://%2/ [R=302,L]
# Otherwise, internally rewrite the request to subdirectory (if not already)
RewriteCond %{HTTP_HOST} ^(.*)\.example\.com
RewriteRule ^((?!Subdomains/).*)$ Subdomains/%1/$1 [L]
However, this is arguably detrimental to your users. A custom 404 response might be preferable.

RewriteCond according to the first part of url not working

I'm trying to work on doing some rewrite but it's not working. Here is my code:
RewriteCond %{REQUEST_URI} !^(static/|server/|internal.php).*$
RewriteRule ^(.*)$ /internal.php?request=$1 [L]
I'm trying to redirect everything to /internal.php?request=blablabla, except the internal.php itself, and things in two folders called static and server, since these two folders have images and so on.
For example,
/hello/world => /internal.php?request=hello/world/
/static/a/b/c/a.jpg => /static/a/b/c/a.jpg not changed
But the code is not working, the RewriteCond seems not able to restrict rewrite of internal.php, and the two folders. Now what's happening is everything is going to rewrite to internal.php, and internal.php would be rewrite to internal.php again. And finally give me a 500 after infinite loops. Which I don't want any rewrite happen. What's wrong?
You are missing a leading / in the request URI expression, also you should escape the dot in internal.php so that it actually matches a dot instead of every char:
RewriteCond %{REQUEST_URI} !^/(static/|server/|internal\.php).*$
RewriteRule ^(.*)$ /internal.php?request=$1 [L]
Note that this will also rewrite /static and /server where the trailing slash is omitted, if you want to avoid that you could for example add another condition:
RewriteCond %{REQUEST_URI} !^/(static|server)$
RewriteCond %{REQUEST_URI} !^/(static/|server/|internal\.php).*$
Tough it should be possible to put this in a single expression, however I'm not that experienced with regular expressions, so I'm pretty sure that this not the most elegant way:
RewriteCond %{REQUEST_URI} !^/(((static|server)(/.*)?)|(internal\.php.*))$

Stop hotlinking using htaccess and non-specific domain code

I need to write an anti-hotlink command for my .htaccess file but it can not be specific to any domain name in particular. Here's what I found on another sites so far but I'm not sure exactly why it doesn't work, can anyone spot the problem?
# Stop hotlinking.
#------------------------------
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} ^https?://([^/]+)/ [NC]
# Note the # is just used as a boundary. It could be any character that isn't used in domain-names.
RewriteCond %1#%{HTTP_HOST} !^(.+)#\1$
RewriteRule \.(bmp|gif|jpe?g|png|swf)$ - [F,L,NC]
Try this.
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} ^https?://(www\.)?([^/]+)/.*$ [NC]
RewriteCond %2#%{HTTP_HOST} !^(.+)#(www\.)?\1$ [NC]
RewriteRule \.(bmp|gif|jpe?g|png|swf)$ - [F,L,NC]
Would even work when only one of the referrer or target url has a leading www.
EDIT : (how does this % thing work?)
%n references the n(th) bracket's matched content from the last matched rewrite condition.
So, in this case
%1 = either www. OR "" blank (because it's optional; used ()? to do that)
%2 = yourdomain.com (without www always)
So, now the rewrite condition actually tries to match
yourdomain.com#stealer.com OR yourdomain.com#www.stealer.com
with ^(.+)#(www\.)?\1$ which means (.+)# anything and everything before # followed by www. (but again optional); followed by \1 the first bracket's matched content (within this regex; not the rewrite condition) i.e. the exact same thing before #.
So, stealer.com would fail the regex while yourdomain.com would pass. But, since we've negated the rule with a !; stealer.com passes the condition and hence the hot-link stopper rule is applied.

.htaccess rewrite rule handled correctly

I want to redirect rewrite /zp-core/admin.php to admin but somehow I can't get it to work.
I've added this rule to my htaccess:
RewriteEngine On
RewriteBase /
RewriteRule ^/admin$ /zp-core/admin.php [L]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^albums/?(.+/?)?$ $1 [R=301,L]
...
# Catch-all - everything else gets handled in PHP for compatibility.
RewriteRule ^(.*)/?$ index.php?album=$1 [L,QSA]
The third rule should do it, but somehow it doesn't. In the end it falls back on the catch all rule
When you say "rewrite A to B", usually, "A" is what the user types and "B" is where you want him to get.
If A='/admin' and B='/zp-core/admin.php' then the first rule is ok, if you remove other rules, it will work.
The problem is - apache looks into .htaccess on every request, including sub-requests, and your redirect does a sub-request... So it does not match the first rule anymore, but then comes the catch-all rule, and it is a match again (you'll see that $_GET["album"] will be /zp-core/admin.php when hitting index.php on such request.
What you need to do is to add RewriteCond either checking that the uri is not /zp-core/admin.php OR that you're not in sub-request...
Either:
RewriteCond %{REQUEST_URI} !^/zp-core/admin.php$
OR:
RewriteCond %{IS_SUBREQ} !=true

mod_rewrite regex (too many redirects)

I am using mod_rewrite, to convert subdomains into directory urls. (solution from here). When I explicity write a rule for one subdomain, it works perfectly:
RewriteCond %{HTTP_HOST} ^[www\.]*sub-domain-name.domain-name.com [NC]
RewriteCond %{REQUEST_URI} !^/sub-domain-directory/.*
RewriteRule ^(.*) /sub-domain-directory/$1 [L]
However, if I try to match all subdomains, it results in 500 internal error (log says too many redirects). The code is:
RewriteCond %{HTTP_HOST} ^[www\.]*([a-z0-9-]+).domain-name.com [NC]
RewriteCond %{REQUEST_URI} !^/%1/.*
RewriteRule ^(.*) /%1/$1 [L]
Can anyone suggest what went wrong and how to fix it?
Your second RewriteCond will never return false, because you can't use backreferences within your test clauses (they're compiled during parsing, making this impossible since no variable expansion will take place). You're actually testing for paths beginning with the literal text /%1/, which isn't what you wanted. Given that you're operating in a per-directory context, the rule set will end up being applied again, resulting in a transformation like the following:
path -> sub/path
sub/path -> sub/sub/path
sub/sub/path -> sub/sub/sub/path
...
This goes on for about ten iterations before the server gets upset and throws a 500 error. There are a few different ways to fix this, but I'm going to chose one that most closely resembles the approach you were trying to take. I'd also modify that first RewriteCond, since the regular expression is a bit flawed:
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com$ [NC]
RewriteCond %1 !=www
RewriteCond %1#%{REQUEST_URI} !^([^#]+)#/\1/
RewriteRule .* /%1/$0 [L]
First, it checks the HTTP_HOST value and captures the subdomain, whatever it might be. Then, assuming you don't want this transformation to take place in the case of www, it makes sure that the capture does not match that. After that, it uses the regular expression's own internal backreferences to see if the REQUEST_URI begins with the subdomain value. If it doesn't, it prepends the subdomain as a directory, like you have now.
The potential problem with this approach is that it won't work correctly if you access a path beginning with the same name as the subdomain the request is sent to, like sub.example.com/sub/. An alternative is to check the REDIRECT_STATUS environment variable to see if an internal redirect has already been performed (that is, this prepending step has already occurred):
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com$ [NC]
RewriteCond %1 !=www
RewriteCond %{ENV:REDIRECT_STATUS} =""
RewriteRule .* /%1/$0 [L]

Resources