htaccess regex matching path containing 'foo' without 'bar' in it - regex

I am trying to block some URL via a .htaccess file with RedirectMatch 403.
Anything that contains YES should be matched only if NO is not in the URL too. Some examples of matching URLs or not:
/dir/YES/ -> yes
/YES/file.ext -> yes
/dir/NO/dir/YES/file.ext -> no
/NO/dir/dir/YES/file.ext -> no
Also there is an unknown number of dir between /YES and /NO.
I've tried various lookbehind and patterns like:
(?<!themes/)(vendor)
(?<=[^themes])(/vendor)
(?<![themes/])(/|/[^themes]+/)vendor(/|$)
But am struggling to get anything working and wondering if this is actually a good idea.

Use it with a specific RewriteCond like this:
RewriteCond %{REQUEST_URI} !/NO/ [NC]
RewriteRule (^|/)YES(/.*|)$ - [F,L,NC]

Why you're patterns aren't working:
(?<!themes/)(vendor)
Will only discard the match if themes/ is immediately to the left of vendor. Lookbehinds do not traverse the entire string automtically.
(?<=[^themes])(/vendor)
Character classes don't work like this. [^themes] matches a single character that is not one of e, h, m, s or t.
What you would want is (?<!themes.*/)(vendor) but only .NET allows lookbehinds of arbitrary length.
The trick is to start at the beginning and make sure that there is no NO on the way to YES using lookaheads:
^(?!.*NO).*(YES)
or
^(?!.*themes).*(vendor)
as lookaheads can be of variable length. If NO is allowed to appear after YES, you have to check at every single character on the way to YES:
^((?!NO).)*(YES)
^((?!themes).)*(vendor)
EDIT: anubhava's solution is actually much neater. If you're ever in need of a single-regex solution you can use mine as a reference.

Related

how to consider regex expression as block to apply lookbehids and lookaheads?

I'm trying to turn a string of this type:
http://example.com/mypage/272-16+276-63+350-02
where aaa-bb are product codes and their numbers may vary from 2 to anything, but I doubt there will ever be more than 5 into:
http://example.com/mypage/272-16+276-63+350-02/?skus=272-16+276-63+350-02
using a redirect match. I'm fairly new to regular expressions and I don't seem to be able to get the negative lookahead and lookbehind to work the way I want.
To capture the string the first time is fairly easy, I used ([\-\+0-9]+) but I don't want it to match on redirection (when I already have a ? in my link). Using ([\-\+0-9]+)(?!\?)(?<\?) doesn't do the trick, it only excludes my last digit from the match. So, is there a way to make regex consider all my product codes as one block, so I can than check if there is a question mark before or after it?
Thank you for looking into this.
You can't mix mod_rewrite (RewriteCond) and mod_alias (RedirectMatch) together. You need to stick with one or the other and you can't match the query string with a RedirectMatch, so you're using mod_rewrite:
RewriteEngine On
RewriteCond %{QUERY_STRING} !skus=
RewriteRule ^mypage/([\-\+0-9]+)$ /mypage/$1?skus=$1 [L,R=301]
Maybe try (?<=http://example.com/mypage/)[0-9+-]+$ Will match only the first case.

More efficient RewriteRule for messy URL

I have developed a new web site to replace an existing one for a client. Their previous site had some pretty nasty looking URLs to their products. For example, an old URL:
http://mydomain.com/p/-3-0-Some-Ugly-Product-Info-With-1-3pt-/a-arbitrary-folder/-18pt/-1-8pt-/ABC1234
I want to catch all requests to the new site that use these old URLs. The information I need out of the old URL is the ABC1234 which is the product ID. To clarify, the old URL begins with /p/ followed by four levels of folders, then the product ID.
So for example, the above URL needs to get rewritten to this:
http://mydomain.com/shop/?sku=ABC1234
I'm using Apache 2.2 on Linux. Can anyone point me on the correct pattern to match? I know this is wrong, but here is where I am currently at:
RewriteRule ^p/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)?$ shop/?sku=$5 [R=301,NC,L]
I'm pretty sure that the pattern used to match each of the 4 folders is redundant, but I'm just not that sharp with regex. I've tried some online regex evaluators with no success.
Thank you.
--EDIT #1--
Actually, my RewriteRule above does work, but is there a way to shorten it up?
--EDIT #2--
Thanks to ddr, I've been able to get this expression down to this:
RewriteRule ^p/([\w-]+/){4}([\w-]+)$ shop/?_sku=$2 [R=301,NC,L]
--EDIT #3--
Mostly for the benefit of ddr, but I welcome anyone to assist who can. I'm dealing with over 10,000 URLs that need to be rewritten to work with a new site. The information I've provided so far still stands, but now that I am testing that all of the old URLs are being rewritten properly I am running into a few anomolies that don't work with the RewriteRule example provided by ddr.
The old URLs are consistent in that the product ID I need is at the very end of the URL as documented above. The first folder is always /p/. The problem I am running into at this point is that some of the URLs have a URL encoded double quote ("). Additionally, some of the URLs contain a /-/ as one of the four folders mentioned. So here are some examples of the variations in the old URLs:
/p/-letters-numbers-hyphens-88/another-folder/-and-another-/another-18/ABC1234
/p/-letters-numbers-hyphens-88/2%22/-/-/ABCD1234
/p/letters-numbers-hyphens-1234/34-88/-22/-/ABCD1234/
Though the old URLs are nasty, I think it is safe to say that the following are always true:
Each begins with /p/
Each ends with the product ID that I need to isolate.
There are always four levels of folders between /p/ and the product ID.
Some folders in between have hyphens, some don't.
Some folders in between are a hyphen ONLY.
Some folders in between contain a % character where they are URL encoded.
Some requests include a / at the very end and some do not.
The following rule was provided by ddr and worked great until I ran into the URLs that contain a % percent sign or a folder with only a hyphen:
RewriteRule ^p/(?:[\w-]+/){4}([\w-]+)$ shop/?_sku=$1 [R=301,NC,L]
Given the rule above, how do I edit it to allow for a folder that is hyphen only (/-/) or for a percent sign?
You can use character classes to reduce some of the length. The parentheses (capture groups) are also unnecessary, except the last one, as #jpmc26 says.
I'm not especially familiar with Apache rules, but try this:
RewriteRule ^p/(?:[\w-]+/){4}([\w-]+)$ shop/?sku=$1 [R=301,NC,L]
It should work if extended regular expressions are supported.
\w is equivalent to [A-Za-z0-9_] and you don't need to not capture underscores, so that's one replacement.
The {4} matches exactly four repetitions of the previous group. This is not always supported so Apache may not like it.
The ?: is optional but indicates that these parens should not be treated as a capture. Makes it slightly more efficient.
I'm not sure what the part in [] at the end is for but I left it. I can't see why you'd need a ? before the $, so I took it out.
Edit: the most compact way, if Apache likes it, would probably be
RewriteRule ^p(/[\w-]+){5}$ shop/?sku=$5 [R=301,NC,L]
EDIT: response to edit 3 of the question.
I'm surprised it doesn't work with only -. The [\w-]+ should match even where there is just a single -. Are you sure there isn't something else going on in these URLs?
You might also try replacing - in the regex with \-.
As for the %, just change [\w-] to [\w%-]. Make sure you leave the - at the end! Otherwise the regex engine will try to interpret it as part of a char sequence.
EDIT 2: Or try this:
RewriteRule ^p/(?:.*?/){4}(.*?)/?$ shop/?sku=$1 [R=301,NC,L]

How do I create a regular expression to negate a string but include anything else

I'm trying to create a regex for apache that will ignore certain strings, but will use anything else. Ive tried many different methods however i just can't seem to get it correct.
for example
i want it to ignore
ignore.mysite.com
but anything else i want it to use
*.mysite.com
If you want a regex that matches whatever.mysite.com where whatever is any possible hostname, but you want the regex not to match ignore.mysite.com, then try this:
^(?!ignore)[a-z0-9-]+\.mysite\.com
The trick is to use negative lookahead.
If you want this regular expressions for mod_rewrite, you can use RewriteCond with negation, just like this:
RewriteCond %{REMOTE_HOST} !^ignore.example.com$
RewriteRule ....
You can find more, of course, in documentation.

Regex to match a URL pattern for a htaccess file

I'm really struggling with this regex.
We're launching a new version of our "groups" feature, but need to support all the old groups at the same URL. Luckily, these two features have different url patterns, so this should be easy? Right?
Old: domain.com/group/$groupname
New: domain.com/group/$groupid/$groupname
The current regex I was using in the regex to redirect old groups to the old script was: RewriteRule ^(group/([^0-9/].*))$ /old_group.php/$1 [L,QSA]
It was working great until a group popped up called something like: domain.com/group/2009-neato-group
At which point the regex matched and forced the new group to the old code because it contained numbers. Ooops.
So, any thoughts on a regex that would match the old pattern so I can redirect it?
Patterns it must match:
group/my-old-group
group/another-old-group/
group/123-another-old-group-456/a-module-inside-the-group/
Patterns it cannot match:
group/12/a-new-group
group/345/another-new-group/
group/6789/a-3rd-group/module-abc/
The best way I've thought about it is that it cannot start with "group-slash-numbers-slash", but haven't been able to turn that into a regex. So, something to negate that pattern would probably work.
Thanks
Think the other way around: If the url matches ^group/[0-9]+/.+, then it is a new group, so redirect these first (with a [L] rule). Everything else must be a old group, you can keep your old rules there.
Your design choices make a pure-htaccess solution difficult, especially since group/123-another-old-group-456/a-module-inside-the-group/ is a valid old-style URL.
I'd suggest a redirector script that looks at its arguments, determines if the first part is a valid groupid, and if so, it's a new-style URL takes the user to that group. Else, it is a old-style URL, so hand it off to old_group.php. Something like this:
RewriteRule ^group/(.*)$ /redirector.php/$1 [L,QSA]

RegEx match replace help

I am trying to do a regex match and replace for an .htaccess file but I can't quite figure out the replace bit. I think I have the match part right, but maybe someone can help.
I have this url-
http://www.foo.com/MountainCommunities/Lifestyles/5/VacationHomeRentals.aspx
And I'm trying to turn it into this-
http://www.foo.com/mountain-lifestyle/Vacation-Home-Rentals.aspx
(MountainCommunities/Lifestyles)/\d/(.*)(.aspx)
and then I figured I would have a rewrite rule starting like this-
mountain-lifestyle/$2$3
but I need to take what is in $2 in this instance and rewrite it to place dashes between the words with capital letters. Now I'm stumped.
I think you'll have to do it in two bits... Take out $2, precede every capital (apart from the first) with a -, then use just append the result to http://www.foo.com/mountain-lifestyle/ with a .aspx on the end.
Try this:
RewriteRule ^(([A-Z][a-z]+-)*)([A-Z][a-z]+)(([A-Z][a-z]+)+)(\.aspx)?$ /$1$3-$4 [N]
RewriteRule ^([A-Z][a-z]+-)+[A-Z][a-z]+$ /$0.aspx [R=301]
Note that mod_rewrite uses an internal counter to detect and avoid infinit loops. So your URL may not contain too much words having to be converted (see MaxRedirects option for Apache < 2.1 and LimitInternalRecursion directive for Apache ≥ 2.1).
I don't think what your doing with the capital letters is possible with regex...
You would be better keeping the dashes in the URL and removing the .aspx
eg: http://www.foo.com/MountainCommunities/Lifestyles/5/Vacation-Home-Rentals
This would require the following rule:
^/MountainCommunities/Lifestyles/5/([^/]+)/\?([^/]+) /mountain-lifestyle/$1.aspx?$2 [I]
This also takes into account any querystrings that are sent to the page.
BTW: How are you using .htaccess with IIS?
You can use the regular expression "([A-Z])" on the middle bit "VacationHome", replacing with the regex "-$1" - This will give you "-Vacation-Home-Rentals" - Then you can just chop off the first character, and stick the first part of the URL on the front, and .aspx on the end.
I think the main regex has been written by others, but to match the request name to place dashes (assuming all the file names have a three-name camel cased representation ala 'VacationHomeRentals.aspx':
RewriteRule: ^/MountainCommunities/Lifestyles/\d+/([A-Z][a-z]+)([A-Z][a-z]+)([A-Z][a-z]+)\.aspx$ /mountain-lifestyle/$1-$2-$3.aspx
This is a restricted version of #Gumbo's response, as I have not had a chance to test his recursion. The recursion technique is definitely the best and most usable for any scenario.
I don't think I quite understand what you are trying to do. Why can't you simply search for:
http://www.foo.com/MountainCommunities/Lifestyles/5/VacationHomeRentals.aspx
and replace it with:
http://www.foo.com/mountain-lifestyle/Vacation-Home-Rentals.aspx ?
Or is this a specific example of a patten you are trying to transform?