Combination of rewrites in .htaccess doesn't work - apache

I have different rules in my .htaccess file which work fine individually but combined in one file they don't.
Here are some examples of my file:
# take care of %C2%A0
RewriteRule ^(.+)\xc2\xa0(.+)$ $1-$2 [L,NE]
# executes **repeatedly** as long as there are more than 1 spaces in URI
RewriteRule "^(\S*) +(\S* .*)$" $1-$2 [L,NE]
# executes when there is exactly 1 space in URI
RewriteRule "^productdetails/617/6/(\S*) (\S*?)/?$" /$1-$2/302 [L,R=302,NE]
Also I've got the following:
RewriteCond %{QUERY_STRING} ^pid=617/?$
RewriteRule ^productdetails\.asp$ /Casio-CDP120-Digital-Piano-in-Black/302? [L,NC,R=301]
which still work fine.
I have now added the following:
RewriteRule "^categories/3/Kawai Digital Pianos/?$" /Compare/Kawai-Digital-Pianos [L,NC,R=301]
which used to rewrite:
mysite.co.uk/categories/3/Kawai%20Digital%20Pianos/ to mysite.co.uk/Compare/Kawai-Digital-Pianos
this does not work anymore
Any help to get the last rule working in combination with the others would be great

You just need to make sure order of rules is correct. For your examples following order should work:
RewriteRule "^categories/3/Kawai Digital Pianos/?$" /Compare/Kawai-Digital-Pianos [L,NC,R=301]
RewriteCond %{QUERY_STRING} ^pid=617/?$
RewriteRule ^productdetails\.asp$ /Casio-CDP120-Digital-Piano-in-Black/302? [L,NC,R=301]
# take care of %C2%A0
RewriteRule ^(.+)\xc2\xa0(.+)$ $1-$2 [L,NE]
# executes **repeatedly** as long as there are more than 1 spaces in URI
RewriteRule "^(\S*) +(\S* .*)$" $1-$2 [L,NE]
# executes when there is exactly 1 space in URI
RewriteRule "^productdetails/617/6/(\S*) (\S*?)/?$" /$1-$2/302 [L,R=301,NE]

Related

Htaccess rules get overriden

I want my .htaccess in the web server's root dir to redirect any request starting with ^assets/(.*)$ to the folder public/$1. All other requests have to go to index.php?request=$1. I also want to keep get parameters (L flag does it) in this case.
RewriteEngine on
RewriteBase /
RewriteRule ^assets/(.*)$ public/$1 [QSA,L]
RewriteRule ^(?!assets)(.*)$ index.php?request=$1 [L,QSA]
The only problem is that the last line overrides the previous :/
I also tested with (.*) but without success.
Thanks for your help.
Try this:
RewriteEngine on
RewriteRule "^/assets/(.*)$" "/public/$1" [L,QSA]
RewriteCond %{REQUEST_URI} "!^/assets/"
RewriteCond %{REQUEST_URI} "!^/index.php"
RewriteCond %{REQUEST_URI} "!^/public"
RewriteRule (.*) "/index.php?request=$1" [L,QSA]
QSA: appends the parameters to the rewritten part
L: last. Which means if it matches, do not process any other rewrite.
Rewrites start at /
To apply a condition on a RewriteRule, use RewriteCond
References:
https://httpd.apache.org/docs/2.4/rewrite/flags.html
https://httpd.apache.org/docs/current/mod/mod_rewrite.html
The first rule says:
if it starts with "/assets/", anything following the second / will be rewritten as "/public/$1".
The RewriteCond and RewriteRule says:
if it does not start by "/assets/", take whatever you received, add it at the end of "/index.php?request=".
So http://www.example.com/directory/somepage.html will go to http://www.example.com/index.php?request=/directory/somepage.html.
If there are parameters in the request, they are appended at the end like so:
http://www.example.com/directory/somepage.html?a=1&b=4 will go to
http://www.example.com/index.php?request=/directory/somepage.html?a=1&b=4
EDIT Nov 19 1115
Added a second RewriteCond to make sure "/index.php" does not rewrite to itself. And a third, in case you want "/public" to also not redirect to "/index.php".

RewriteCond to match query string parameters in any order

I have a URL which may contain three parameters:
?category=computers
&subcategory=laptops
&product=dell-inspiron-15
I need 301 redirect this URL to its friendly version:
http://store.example.com/computers/laptops/dell-inspiron-15/
I have this but cannot make it to work if the query string parameters are in any other order:
RewriteCond %{QUERY_STRING} ^category=(\w+)&subcategory=(\w+)&product=(\w+) [NC]
RewriteRule ^index\.php$ http://store.example.com/%1/%2/%3/? [R,L]
You can achieve this with multiple steps, by detecting one parameter and then forwarding to the next step and then redirecting to the final destination
RewriteEngine On
RewriteCond %{QUERY_STRING} ^category=([^&]+) [NC,OR]
RewriteCond %{QUERY_STRING} &category=([^&]+) [NC]
RewriteRule ^index\.php$ $0/%1
RewriteCond %{QUERY_STRING} ^subcategory=([^&]+) [NC,OR]
RewriteCond %{QUERY_STRING} &subcategory=([^&]+) [NC]
RewriteRule ^index\.php/[^/]+$ $0/%1
RewriteCond %{QUERY_STRING} ^product=([^&]+) [NC,OR]
RewriteCond %{QUERY_STRING} &product=([^&]+) [NC]
RewriteRule ^index\.php/([^/]+/[^/]+)$ http://store.example.com/$1/%1/? [R,L]
To avoid the OR and double condition, you can use
RewriteCond %{QUERY_STRING} (?:^|&)category=([^&]+) [NC]
as #TrueBlue suggested.
Another approach is to prefix the TestString QUERY_STRING with an ampersand &, and check always
RewriteCond &%{QUERY_STRING} &category=([^&]+) [NC]
This technique (prefixing the TestString) can also be used to carry forward already found parameters to the next RewriteCond. This lets us simplify the three rules to just one
RewriteCond &%{QUERY_STRING} &category=([^&]+) [NC]
RewriteCond %1!&%{QUERY_STRING} (.+)!.*&subcategory=([^&]+) [NC]
RewriteCond %1/%2!&%{QUERY_STRING} (.+)!.*&product=([^&]+) [NC]
RewriteRule ^index\.php$ http://store.example.com/%1/%2/? [R,L]
The ! is only used to separate the already found and reordered parameters from the QUERY_STRING.
I take a slightly different approach for this sort of thing, leveraging ENV VARs set and read by mod_rewrite. I find it more readable / maintainable to refer to the backreferences by name like this, and these ENV VARs can be reused later in request processing too. Overall I think it's a more powerful and flexible approach than the accepted answer here. In any case, it works well for me. I've copied my gist below in its entirety:
From https://gist.github.com/cweekly/5ee064ddd551e1997d4c
# Mod_rewrite is great at manipulating HTTP requests.
# Using it to set and read temp env vars is a helpful technique.
#
# This example walks through fixing a query string:
# Extract good query params, discard unwanted ones, reorder good ones, append one new one.
#
# Before: /before?badparam=here&baz=w00t&foo=1&bar=good&mood=bad
# After: /after?foo=1&bar=good&baz=w00t&mood=happy
#
# Storing parts of the request (or anything you want to insert into it) in ENV VARs is convenient.
# Note the special RewriteRule target of "-" which means "no redirect; simply apply side effects"
# This lets you manipulate the request at will over multiple steps.
#
# In a RewriteRule, set custom temp ENV VARs via [E=NAME:value]
# Note it's also possible to set multiple env vars
# like [E=VAR_ONE:hi,E=VAR_TWO:bye]
#
# You can read these values using %{ENV:VAR_NAME}e <- little "e" is not a typo
#
# Tangent:
# Note you can also read these env vars the same way, if you set them via SetEnvIf[NoCase]
# (It won't work to use SetEnv, which runs too early for mod_rewrite to pair with it.)
#
# Regex details:
# (?:) syntax means "match but don't store group in %1 backreference"
# so (?:^|&) is simply the ^ beginning or an & delimiter
# (the only 2 possibilities for the start of a qs param)
# ([^&]+) means 1 or more chars that are not an & delimiter
RewriteCond %{QUERY_STRING} (?:^|&)foo=([^&]+)
RewriteRule ^/before - [E=FOO_VAL:%1]
RewriteCond %{QUERY_STRING} (?:^|&)bar=([^&]+)
RewriteRule ^/before - [E=BAR_VAL:%1]
RewriteCond %{QUERY_STRING} (?:^|&)baz=([^&]+)
RewriteRule ^/before - [E=BAZ_VAL:%1]
RewriteRule ^/before /after?foo=%{FOO_VAL}e&bar=%{BAR_VAL}e&baz=%{BAZ_VAL}e&mood=happy [R=301,L]
P.S. This is not a copy/pasteable solution to your question, but rather shows exactly how to handle this kind of problem. Armed w this understanding, leveraging it for your example will be completely trivial. :)
1) In case You just need to check that all parameters are in url:
RewriteCond %{QUERY_STRING} (^|&)category\=computers($|&)
RewriteCond %{QUERY_STRING} (^|&)subcategory\=laptops($|&)
RewriteCond %{QUERY_STRING} (^|&)product\=dell\-inspiron\-15($|&)
RewriteRule ^$ http://store.example.com/computers/laptops/dell-inspiron-15/? [R=301,L]
2) In case You need exact set of parameters:
RewriteCond %{QUERY_STRING} ^&*(?:category\=computers|subcategory\=laptops|product\=dell\-inspiron\-15)(?!.*&\1(?:&|$))(?:&+(category\=computers|subcategory\=laptops|product\=dell\-inspiron\-15)(?!.*&\1(?:&|$))){2}&*$
RewriteRule ^$ http://store.example.com/computers/laptops/dell-inspiron-15/? [R=301,L]
This rule is generated by 301 redirect generator

Ignore requests from internal redirects

RewriteRule ^resources/.+$ - [L]
RewriteRule .? index.php?t=$0 [QSA,L]
Would produce a 500 - Internal Server Error, because it would repeat again and again the same rule, due to internal redirected requests which are exactly treated as the first one. It would lead to an infinite chain of index.php?t=index.php&t=index.php&t=index.php&[...infinite more...]&t=test.php
But in my opinion this is not much better:
RewriteRule ^resources/.+$ - [L]
RewriteCond %{QUERY_STRING} !t=
RewriteCond %{REQUEST_URI} !^index\.php$
RewriteRule .? index.php?t=$0 [QSA,L]
Because now the user could input index.php?t=test.php as address, would pass the script and get the same content as if he had given test.php. I don't like that.
So how do I execute the first one without the issue of repeating internal redirects? Surely, a flag VL - Very Last would do the trick but sadly it does not exist.
First we have a look at all parameters given to the rules possibly indicating whether this is a chained request or not. This means, we either 1) need a variable changed in chained requests not relative to the changed URI or 2) the opposite, a variable which is relative to the changed URI and did not change (because we can compare it then against the others who did chage).
The problem is, they almost all update according to the applied RewriteRules.
IS_SUBREQ (1) and THE_REQUEST (2) are the only interesting variables but sadly internal redirects are not treated as subrequests, so IS_SUBREQ disappears. Only THE_REQUEST does not change and contains the real given path, so we have found our entry point.
With this in mind here is the annoying complex solution:
RewriteEngine On
# Set SCRIPT_URI and SUBREQ
# MUST be the first statements in the file
# SCRIPT_URI is the original browser-requested path
# SUBREQ is "true" if the original browser-requested path is not overriden yet
RewriteCond %{ENV:REQUEST_PARSED} !true
RewriteCond %{THE_REQUEST} ^\s*\w+\s+(http://[^\s/]+/|/?)([^\s\?]*)[\s\?$]
RewriteRule .? - [E=SCRIPT_URI:/%2,C]
RewriteRule .? - [E=REQUEST_PARSED:true]
RewriteCond %{ENV:SCRIPT_URI} ^(.*?)/\.($|/.*$)
RewriteRule .? - [E=SCRIPT_URI:%1%2,N]
RewriteCond %{ENV:SCRIPT_URI} ^(.*?)/[^/]+/\.\.($|/.*$)
RewriteRule .? - [E=SCRIPT_URI:%1%3,N]
RewriteCond %{ENV:SCRIPT_URI} ^(.*?)//\.\.($|/.*$)
RewriteRule .? - [E=SCRIPT_URI:%1/%2,N]
RewriteCond %{ENV:SCRIPT_URI}#%{REQUEST_URI} !^/*(.*)#/*\1$
RewriteRule .? - [E=SUBREQ:true]
# SCRIPT_URI and SUBREQ are set now. Actual content follows:
RewriteCond %{ENV:SUBREQ} !true
RewriteRule ^resources/.+$ - [L]
RewriteCond %{ENV:SUBREQ} !true
RewriteRule .? index.php?t=$0 [QSA,L]

Apache mod_rewrite going berserk - redirecting where it shouldn't

I have a script that echoes a meta redirect to a page called account_management.php5, but for some reason it automatically redirects from there to index.php5. My .htaccess file handles a couple of redirects automatically, for example index.html|php5 to the domain root, and that's the only place I can see this problem originating, but I don't understand why. This is my .htaccess file:
RewriteEngine On
#remember to change this to aromaclear
RewriteCond %{HTTP_HOST} !^sinaesthesia\.co.uk$ [NC]
RewriteRule ^(.*)$ http://sinaesthesia.co.uk/$1 [R=301,L]
RewriteCond %{THE_REQUEST} ^GET\ .*/index\.(php5|html)\ HTTP
RewriteRule ^(.*)index\.(php5|html)$ /$1 [R=301,L]
#translate any .html ending into .php5
RewriteRule ^(.*)\.html$ /$1\.php5
#change / for ?
RewriteRule ^(.*)\.html/(.*)$ /$1\.html?$2
#strip .html from search res page
RewriteRule ^(.*)search/(.*)$ /$1search_results\.html/search=$2
#translate product details link from search res page
RewriteRule ^products/(.*)/(.*)/(.*)$ /product_details.php5?category=$1&title=$2&id=$3 [L]
#Translate products/psorisis/chamomile-skin-cream-P[x] to productview.php5?id=1
RewriteRule ^products/.*-P([0-9]+) /productview.php5?id=$1 [L]
Wrong:
RewriteRule ^(.*)\.html$ /$1\.php5
Right:
RewriteRule ^(.*)\.html$ /$1.php5
Righter:
RewriteRule ^(.*)\.html$ /$1.php5 [QSA]
This same mistake of escaping special chars in the second param of RewriteRule is happening in other rules too, I don't know if apache will handle it, but I know you don't need it because second param is not a regexp.
Never compare to %{THE_REQUEST}, thats a weird thing to do, you don't need that. Moreover, this condition is fine without it. Just put there:
RewriteRule ^(.*)index\.(php5|html)$ $1 [R=301,QSA,L]
Now look at it:
RewriteRule ^(.*)\.html/(.*)$ /$1.html?$2
First, you are still accepting that there are references to .html files, just after trying to translate all .html to .php5, there's something wrong here.
Moreover, you are defineing as QueryString something that was originally a file path, and are not even putting it in a key. It won't work, it need some more treatment.
#strip .html from search res page
RewriteRule ^(.*)search/(.*)$ /$1search_results.html/search=$2
Wasn't it supposed to strip the .html? Because it is actually putting a .html there. Maybe as it is not an [L] it get fixed in the next loop, but you could just get all fixed right here.
#translate product details link from search res page
RewriteRule ^products/(.*)/(.*)/(.*)$ /product_details.php5?category=$1&title=$2&id=$3 [L]
This one full of .* is potentially unstable, specially delimitating the end. You should do this:
RewriteRule ^products/([^/]*)/([^/]*)/([^/]*) /product_details.php5?category=$1&title=$2&id=$3 [L]
# or:
RewriteRule ^products/(.*?)/(.*?)/([^/]*) /product_details.php5?category=$1&title=$2&id=$3 [L]
The last one looks correct, except that you should strip the special character that may be faced as a range delimiter, the "-". I don't think it work after a *, but just to be sure and correct the syntax:
RewriteRule ^products/.*\-P([0-9]+) /productview.php5?id=$1 [L]
Add this just after RewriteEngine on
RewriteLogLevel 9
RewriteLog /tmp/rw.log
Then restart the webserver. It should help you debug the problem.
Edit: Sorry, I didn't notice the .htaccess above. This will only work from the main apache configuration file.

How can I make my .htaccess skip rules conditionally?

I want to redirect all URLs that do not contain "_js","_css", "_img", etc., to my dispatch script. Somehow it doesn't work.
For instance, all files inside my /_js/ folder are unaccessible (meaning: they are sent to index.php instead of getting to the physical files residing in that folder).
Here is my htaccess:
IndexIgnore *
Options +FollowSymLinks
RewriteEngine on
# if the following conditions are met, SKIP the rewriteRules.
RewriteCond %{REQUEST_URI} ^/(_admin/¦_css/¦_js/¦_img/)
RewriteRule . - [S=9]
# Externally redirect to add missing trailing slash
RewriteRule ^(([a-z0-9._\-]+/)*[a-z0-9_\-]+)$ http://%{HTTP_HOST}/$1/?%{QUERY_STRING} [NC,R,L]
# SIX PARAMS
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2&menu=$3&content=$4&id=$5&title=$6&%{QUERY_STRING} [NC,L]
# FIVE PARAMS
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2&menu=$3&content=$4&id=$5&%{QUERY_STRING} [NC,L]
# FOUR PARAMS
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2&menu=$3&content=$4&%{QUERY_STRING} [NC,L]
# THREE PARAMS : projects/touch/texts/
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2&menu=$3&%{QUERY_STRING} [NC,L]
# TWO PARAMS: downloads
RewriteRule ^downloads/([^/]+)/$ index.php?section=downloads&item=$1&%{QUERY_STRING} [NC,L]
# TWO PARAMS:
RewriteRule ^([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2&%{QUERY_STRING} [NC,L]
# TAG URL : index.php?tag=space%2C+navigable+music#5
RewriteRule ^tag/([a-z0-9_\-]+)/$ index.php?tag=$1&%{QUERY_STRING} [NC,L]
# ONE PARAM
RewriteRule ^([a-z0-9_\-]+)/$ index.php?section=$1&%{QUERY_STRING} [NC,L]
Edit:
Note my folder structure. Could it be the problem source?
I have a "v1" and "v2" folder structure. This .htaccess sits in the "v2" folder. one level above, i have a .htaccess that redirects all requests to "v2".
root
L.htaccess << dispatch between v1 and v2 folders
L v1 L v2 L.htaccess << the .htaccess code posted above
L _admin L all my website files & folders
You’re using the wrong character, ¦ (broken bar, U+00A6) instead of | (vertical line, U+007C), and wrong pattern for REQUEST_URI.
RewriteCond %{REQUEST_URI} ^/v2/(_admin/|_css/|_js/|_img/)
RewriteRule . - [S=9]
Or for the .htaccess file in your v2 directory just:
RewriteRule ^_(admin|css|js|img)/ - [S=9]
Here are some random fixes that may or may not fix the actual problem, which is not totally clear (see my comment) and which could be helped if you control the server and enable the RewriteLog, via:
RewriteLog "/tmp/rewrite.log"
RewriteLogLevel 9
but you have to put this in main server configuration.
That said, two main problems:
Lack of use of QSA flag (not really relevant)
Excessive use of slashes (probably relevant in the actual problem, see new version of the skipping rule)
Here goes the modified file
IndexIgnore *
Options +FollowSymLinks
RewriteEngine on
# if the following conditions are met, SKIP the rewriteRules.
RewriteCond %{REQUEST_URI} ^(_admin¦_css¦_js¦_img)
RewriteRule . - [L]
# Externally redirect to add missing trailing slash. Not really needed, AFAICS
# RewriteRule ^(([^/]+/)*[^/]+)$ http://%{HTTP_HOST}/$1/ [NC,R,L,QSA]
# SIX PARAMS
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2&menu=$3&content=$4&id=$5&title=$6 [NC,L,QSA]
# FIVE PARAMS
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2&menu=$3&content=$4&id=$5 [NC,L,QSA]
# FOUR PARAMS
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2&menu=$3&content=$4 [NC,L,QSA]
# THREE PARAMS : projects/touch/texts/
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2&menu=$3 [NC,L,QSA]
# TWO PARAMS: downloads
RewriteRule ^downloads/([^/]+)/?$ index.php?section=downloads&item=$1 [NC,L,QSA]
# TWO PARAMS:
RewriteRule ^([^/]+)/([^/]+)/?$ /index.php?section=$1&item=$2 [NC,L,QSA]
# TAG URL : index.php?tag=space%2C+navigable+music#5
RewriteRule ^tag/([^/]+)/?$ index.php?tag=$1 [NC,L,QSA]
# ONE PARAM
RewriteRule ^([^/]+)/?$ index.php?section=$1 [NC,L,QSA]
EDIT: Given the folder structure explained, try adding to the v2 .htaccess at the beginning the following:
RewriteBase /
You still haven't explained if you can or cannot use RewriteLog (I presume you can't)