Mod rewrite regex: How to allow only one period between other characters? - regex

I am writing a mod rewrite rule to allow only one period between an arbitrary length characters. The characters must begin with a letter, followed by 2-29 characters or numbers, and allow only one period (optional) somewhere in the middle (but never in the end).
Here is what I have so far:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-z]([.a-z0-9]{2,29}))$ user.php?u=$1 [NC,QSA,L]
This rule is not working since it allows things like f.oo (valid), foo. (invalid), f.o.o (invalid), or even f.......
Is it possible to accomplish using just apache's mod rewrite? Or does the check have to be done in PHP or somewhere else?
Similar questions: this one and this one.
Thank you.

I couldn't do it in one rule, so I used two:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 ^.{3,30}$
RewriteRule ^([a-z][a-z0-9]*(?:\.[a-z0-9]+)?)$ user.php?u=$1 [NC,QSA,L]

I can't figure out how to do it in one regex, but you could stack your regex's
^[a-z][.a-z0-9]{2,29}$ #3-30 characters long, starting with a letter, then nums
^[a-z0-9]+\.?[a-z0-9]+$ #has zero or one dot in middle
updated to take numbers into account
http://regex101.com/r/nD2lI6
http://regex101.com/r/gR3vA1
So the full example should be something like:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^[a-z][.a-z0-9]{2,29}$
RewriteRule ^([a-z0-9]+\.?[a-z0-9]+)$ user.php?u=$1 [NC,QSA,L]

Related

.htaccess - Recursively mapping slashes to underscores

G'day,
As the title says, I'm trying to make the url formatted as: /this/is/mah/page/strucutre to the file this_is_mah_page_structure.php.
Now, I have that working - except that I can't know the depth of the structure. Thus I need to have some recursion going on.
The working snipit I have for one and two replacements is:
# map the slashes to underscores
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/([^/]+)/?$ $1_$2 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/?$ $1_$2_$3 [L]
I have found this: Htaccess recursively replace underscores with hyphens which states how to do it (for different characters); and this https://www.askapache.com/htaccess/rewrite-underscores-hyphens-seo-url/ which shows similar code. But I can't make it work.
Also, I found that .htaccess recursion can cause 500 errors - somewhere in the order of 10 loops - based on LimitInternalRecursion. From that, I tried replacing multiple with the one pass, but that had the unexpected outcome of doubling up the url.
This is the example from the other StackOverflow answer to give you an idea of what I've been testing. Any ideas / thoughts / direction?
(Rewrites underscores with hyphens - so it's the wrong side, but it's a start)
RewriteEngine On
# if there is only one underscore then repalce it by - and redirect
RewriteRule ^([^_]*)_([^_]*)$ /$1-$2 [L,R=302]
# if there are more than one underscores then "repeatedly" repalce it by - and set env var USCORE
RewriteRule ^([^_]*)_([^_]*)_(.*) $1-$2-$3 [E=USCORE:1]
# if USCORE env var is set then redirect
RewriteCond %{ENV:REDIRECT_USCORE} =1
RewriteRule ^([^_]+)$ /$1 [L,R=302]
-- Edit and full solution --
So it finally dawned on me through this post, that the presence / absence of R=302 (or 301) is what shifts the idea from "Map URL to file" (ie: transparent); to "Redirect". It should have been obvious, but I thought there was more too it.
The final solution is as #anubhava suggested, but I removed the ,R=301 and added RewriteCond %{REQUEST_FILENAME} !-f and RewriteCond %{REQUEST_FILENAME} !-d to prevent collision between the rules and real files/directories of the same name. (i.e. scripts.js, /images/yomomma.jpg, etc)
# when there are more than one / then "recursively" replace it by _
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/([^/]+/.+)$ $1_$2 [N,DPI]
# when there is only one / then replace it by _ and redirect
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/([^/]+)$ /$1_$2 [NE,L]
I had posted that answer about 5 years ago though now I think it can be further improved.
You may use these rules in site root .htaccess:
RewriteEngine On
# if request is for a file [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]
# if request is for a directory
RewriteCond %{REQUEST_FILENAME} -d
# then ignore all rules below
RewriteRule ^ - [L]
# when there is only one / then replace it by _ and redirect
RewriteRule ^([^/]+)/([^/]+)$ /$1_$2 [NE,L,R=301]
# when there are more than one / then recursively replace / by _
RewriteRule ^([^/]+)/(.+)$ $1_$2 [N,DPI]
Change R=301 rule to RewriteRule ^([^/]+)/([^/]+)$ /$1_$2 [L] if you don't want an external redirect.

Smarter way to write recursive redirect?

I am sure theres a better way to do this, but cant really figure it out.
Can somebody tell me if theres a better way to write to following Apache .htaccess rewrite rules?
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]*)$ /index.php?param1=$1 [L,QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]*)/([^/]*)$ /index.php?param1=$1&param2=$2 [L,QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]*)/([^/]*)/([^/]*)$ /index.php?param1=$1&param2=$2&param3=$3 [L,QSA]
If you just have 3 path segments your current code does not seem that bad. Your current rules are easy to understand, and I don't think the duplicated conditions !-f and !-d will have a huge performance impact.
You can rewrite your current rules to a sequence like this. First you rewrite the url val1/val2/val3/val4/val5 to index.php/val1/val2/val3/val4/val5. Then we have a bunch of similar rules that all take the first path segment and turn it into a parameter. Once no more path segments remain the rest of the rules are ignored.
#Only on requests with at least 1 character (e.g. not to http://localhost)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php%{REQUEST_URI} [QSA]
#Since we don't have the L flag, this will do param1 - paramx in one go
RewriteRule ^index\.php/([^/]+)(/.*)?$ index.php$2?param1=$1 [QSA]
RewriteRule ^index\.php/([^/]+)(/.*)?$ index.php$2?param2=$1 [QSA]
RewriteRule ^index\.php/([^/]+)(/.*)?$ index.php$2?param3=$1 [QSA]
RewriteRule ^index\.php/([^/]+)(/.*)?$ index.php$2?param4=$1 [QSA]
RewriteRule ^index\.php/([^/]+)(/.*)?$ index.php$2?param5=$1 [QSA]
Before using this, you should test both the performance of these rules and your current rules to see which one performs better. Please note that this approach yields strange results if more than, in this case, 5 path segments are in the url, as the 6-th until nth path segment will overwrite param1 - param5. You could 'fix' this by adding another rule RewriteRule ^index\.php index.php [END] on recent versions of Apache.

mod_rewrite .htaccess rewrite match with or without file extension

I'm using a php script to match request links to a site. I'm currently matching on 'jcole/' and 'jcole'. However I'd like to be able to match on "jcole"(.php|.html|.htm), "jcole/", and "jcole". I'd like the match to be agnostic of whatever the file extension maybe and also not care if there are periods in the name.(for example I'd like to be able to match on "j.cole")
Currently I have my .htaccess configured like so:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*\.html)$ /loadlink.php=link=$1 [L,QSA]
RewriteRule ^(.*)/?$ /loadlink.php?link=$1 [L,QSA]
You are way better off doing all of this from within php. Regex is not going to be able to do everything you want, especially having to deal with arbitrary periods in the name. So you'd just want the second rule:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/?$ /loadlink.php?link=$1 [L,QSA]
Note that rewrite conditions only apply to the immediately following RewriteRule.
This will send everything to the "link" parameters, only removing an arbitrary trailing slash if it exists. It's up to the loadlink.php script to get rid of the .php or random periods in the name.

URL rewriting with Apache .htaccess with two matches

Sorry about this question - I know this is asked a lot, but this case is a little more distinct (for me at least).
I have the following content in my current .htaccess:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/?$ index.php?mode=$1 [QSA,L]
This rewrites all URLs that are from index.php and have the mode parameter to a simply URL where the mode's content becomes the main part of the URL, like this:
http://someurl.com/index.php?mode=mymode
Becomes:
http://someurl.com/mymode
This is exactly what I need. But I also need to extend this and be able to achieve the same effect with another file of mine named user.php. The case is almost the same:
http://someurl.com/user.php?action=hello
Becomes:
http://someurl.com/hello
I have no idea how to achieve the second part without conflincting with the first one.
I'm kinda stuck on this one.
Keep your rules like this:
# new rules for /user.php?action=hello
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^user/([^/]+)/?$ user.php?action=$1 [QSA,L]
# existing rule for /index.php?mode=mymode
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/?$ index.php?mode=$1 [QSA,L]
PS: Also better to use absolute path in your css, js, images files rather than arelative one. Which means you have to make sure path of these files start either with http:// or a slash /.

Mod Rewrite help apache - turning pretty URLs into PHP acceptable links

Right now I'm using this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([0-9a-zA-Z-]+)/?$ profile.php?userName=$1 [L]
This works for normal cases without spaces or underscores, etc... just the basics.
I need it to be pretty open to characters though - such as John_doe john-doe john doe.
I've tried a lot of other Regex's, but I can't seem to get one that works for everything. Much appreciate any help!
Thanks.
The . character will match any single character:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/?$ profile.php?userName=$1 [L]
Note the use of this regex is discouraged because of character encoding purposes (and SEO as well, but I don't know if it's relevant to you). It's always better to have addresses containing only lower case alphanumeric characters and dashes (-).
For more info about mod_rewrite regex, see this link.