Rewrite URL term(s) separated by equal sign to query string(s) - regex

Using mod_rewrite, what RewriteRule(s) would make the following two examples function properly?
Example #1 (one term):
http://example.net/dir/thanks
-to-
http://example.net/dir/index.php?a=thanks
Example #2 (two terms):
http://example.net/dir/thanks=stackoverflow
-to-
http://example.net/dir/index.php?a=thanks&b=stackoverflow
Note: The .htaccess file is located in /dir/ outside of domain root.
I've gotten close with this:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]*)=([^/]*)/?$ index.php?a=$1&b=$2 [L]
However, to work with example #1 a trailing "=" is required (which I want to avoid). I've tried changing "=" to "=?" in the regex to make it optional but while it then works for example #1 it fails for example #2.
Thank you kindly for any consideration to my question. I'm new to mod_rewrite and regex and am truly stumped.

I see two main approaches:
1. More safer/easier to understand and extend if necessary. Make two rules: 1st will catch thanks=stackoverflow while 2nd will work with thanks only:
RewriteEngine On
# do not do anything for already existing files
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .+ - [L]
# will work with /thanks=stackoverflow
RewriteRule ^([^/=]+)=([^/=]+)/?$ index.php?a=$1&b=$2 [L]
# will work with /thanks
RewriteRule ^([^/=]+)/?$ index.php?a=$1 [L]
2. Combine those two rules into a single rule. In this case parameter b= will always be present, but will be empty for thanks scenario:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/=]+)(=([^/=]+))?/?$ index.php?a=$1&b=$3 [L]
Tested both -- working fine on my Apache box.

Why not create two seperate rewrite rules:
i) One that takes care of example 1 - with the added check that it does not contain an equal sign [so that you don't match example 2 with rewrite rule 1]
ii) Another one that takes care of example 2

Related

Mod rewrite rule for optional language in URL

I have a shop URL like this
https://domain.eu/cz/lp/9200
And I'm trying to rewrite it in .htaccess to https://domain.eu/lp/index.php?id=$2&lang=$1
I came really close with
RewriteRule ^/?(hr|sk|pl|cz|ro|it)/lp/(\d+)?$ /lp/index.php?id=$2&lang=$1
which works ok but I can't seem to find a way to handle the situation when there is no lang in URL.
So this is also valid: https://domain.eu/lp/9200 but in that case I want $1 to just be empty (or have a default value when it's not present)
I know "?" means "one or zero" times that's why I tried
RewriteRule ^/?[(hr|sk|pl|cz|ro|it)?]/lp/(\d+)?$ /lp/index.php?id=$2&lang=$1
But it doesn't work as expected. Any point in the right direction would be appreciated.
With your shown samples, attempts; please have your htaccess Rules file in following manner. Make sure to clear your browser cache before testing your URLs.
RewriteEngine ON
##Rewrite rule for uris which have only 1 parameter.
RewriteRule ^lp/(\d+)/?$ /lp/index.php?id=$1 [NC,L]
##Rewrite rule for uris which 2 parameters.
RewriteRule ^(hr|sk|pl|cz|ro|it)/lp/(\d+)/?$ /lp/index.php?id=$2&lang=$1 [NC,L]
OR use following solutions, in case uris you are trying to access are non-existent ones. Make sure either use 1st solution OR this one, once at a time only.
RewriteEngine ON
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^lp/(\d+)/?$ /lp/index.php?id=$1 [NC,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(hr|sk|pl|cz|ro|it)/lp/(\d+)/?$ /lp/index.php?id=$2&lang=$1 [NC,L]

.htaccess mod_rewrite skips capture group when group is empty

I'm using .htaccess to rewrite URLs with two groupings.
My .htaccess:
RewriteRule ^tips/([0-9]*)/?([0-9]*)?/?(.+)?$ /tips.php?item=$1&id=$2 [L,QSA]
It works correctly when there are two numbers are in the URL, but when one is empty, it skips the capture group entirely.
example.com/tips/1/2/abc -> /tips.php?item=1&id=2 (EXPECTED)
example.com/tips//2/abc -> /tips.php?item=2&id= (UNEXPECTED)
/tips.php?item=&id=2 (WHAT WAS EXPECTED)
I've put this into a few regex/htaccess testers but they all seem to say it should be working as I expected.
(This of course is an unusual URL, but I want to pass it to PHP so I can handle the error in PHP.
You may use this rewrite rule with a THE_REQUEST variable:
Options -MultiViews
RewriteEngine On
RewriteCond %{THE_REQUEST} \s/+tips/(\d*)/(\d*)/ [NC]
RewriteRule ^ tips.php?item=%1&id=%2 [L,QSA]
mod_rewrite engine converts multiple // into a single / in RewriteRule pattern therefore we need to use THE_REQUEST here that matches against original request received in Apache.
With your shown samples, could you please try following.
Options -MultiViews
RewriteEngine ON
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^tips/(\d+)/(\d+)/?$ tips.php?item=$1&id=$2 [L,QSA]
This will look for non existing files/directories and rewrite them to php file with parameters, like #anubhava sir mentioned multiple /slashes will be converted to single / by rewrite rule so this should work for both of your mentioned cases.

Apache mod_rewrite is removing slashes and generally ignoring my rules

I'm at my wit's end, Stack Overflowers. Trying to do what I thought was a simple rewrite rule to replace slashes in the URL with tilde, then add a ".html" at the end.
So my .htaccess is thus:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/(.+)/(.+)$ $1~$2 [N]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/([^/]+)$ $1.html [L]
Basically I'm running a repeated rule to replace all slashes with tildes one at time, then my final rule adds the ".html" -- because all our web files need to be in one folder (client request--silly, I know).
I've tested the pattern "part-one/part-two/part-three" here: http://martinmelin.se/rewrite-rule-tester/ and it only works if I chop off the initial slash and remove the rewrite conditions (which makes no sense b/c no filename I put in there should exist on that server), but that's not the case on my local server.
It should eventually read the file "part-one~part-two~part-three.html" but when I look at the Apache error log on my local machine, I get this:
File does not exist: /path/to/website/part-one
So it basically chops off the final two parts and never tries to add a ".html" -- so what on earth is going on?? Please help, mod_rewrite gurus!!
The reason why it wants you to remove the leading / is because the rewrite engine strips off the prefix (the leading slash of an URI) before it runs them through rules in an htaccess file. If you were using apache 1.3 or if the rules were in a non-per-directory context in the server or vhost config, then you'd need the leading slash in the rule's pattern:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/(.+)$ /$1~$2 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^([^/]+)$ /$1.html [L]
Additionally, you probably don't want the N flag, as you want rewrite to stop immediately in its current iteration. Also, a condition which first checks if the .html actually exists before you rewrite will prevent 500 internal server errors.

.htaccess RegEx for variable file version

I would like clients to be able to access certain files using an arbitrary version number to bypass caching.
For example, suppose there are two files: styles.css and jquery.min.js They should be able to request either the originals or styles.23.css and jquery.min.5039.css.
The rule I came up with was:
RewriteEngine On
RewriteRule ^(.*)\.(?!\..*)[\d]+\.(.*)$ $1.$2 # strip out version number
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
Broken down, here's my thought on what it should be doing:
^(.*) – starting from the beginning, match all
\. – up to the first period...
(?!\..*) - ...which is not followed by a period and anything,
[\d]+\. – then match if ends in one or more digits followed by a period...
(.*)$ – ...and anything
This RegEx actually works seems to work in PHP but not .htaccess, which has me a bit confused.
Thank you in advance.
Why do you need lookahead etc. Following should work:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+?)\.\d+\.(.*)$ $1.$2 [L]

.htaccess regular expression issue

I still have no answer about this so I'm posting it here.
I want to match an url in the form of root/language/sub/.../document in a .htaccess file and then rewrite it as root/sub/.../document.php?lang=language
Im close to hit but it seems my regexp doesn't 'catch' the input url. Here it is :
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond ^[a-z]{2}.*$/%{REQUEST_FILENAME}\.php -f
RewriteRule ^([a-z]{2})/(.*)$ $2.php?lang=$1
Can someone point me out what is wrong here ? I'm no expert in regexp nor apache rewrites.
Tyvm
* EDIT *
root stands for domain ie mydomain.net
Here are a few examples :
mydomain.net/fr/contact
should be rewriten to
mydomain.net/contact.php?lang=fr
and
mydomain.net/en/articles/view
should be rewriten
mydomain.net/articles/view.php?lang=en
etc...
I believe you are looking for this configuration:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} ^(.*/|)(en|de|fr)/(.*)$ [NC]
RewriteCond %1%3.php -f
RewriteRule ^(.*/|)(en|de|fr)/(.*)$ $1$3.php?lang=$2 [NC,QSA,L]
Explanation:
RewriteCond %{REQUEST_FILENAME} !-d
Checks if requested url is not a directory. If yes, no rewriting will be processed.
RewriteCond %{REQUEST_FILENAME} ^(.*/|)(en|de|fr)/(.*)$ [NC]
Checks if url contains /xx/ part inside, where xx is one en, de or fr. Flag [NC] allows uppercase and lowercase charactes, so for example EN or Fr will be accepted. If there is no such "language" part in the url, no rewriting will be processed.
RewriteCond %1%3.php -f
Checks if %1%3.php file exists, where %1 is (.*/|) and %3 is (.*) matches from the previous RewriteCond ^(.*/|)(en|de|fr)/(.*)$. If such php file does not exist, no rewriting will be processed.
RewriteRule ^(.*/|)(en|de|fr)/(.*)$ $1$3.php?lang=$2 [NC,QSA,L]
In the RewriteRule left condition will be matched if it came to this line, as it was already checked with RewriteCond, so now it will process rewriting to php file, adding lang part, but because there is also [QSA] flag, it will keep also other GET parameters, so lang will be added to existing parameters. Flag [L] says it is last rewriting rule that should apply and no more rewriting will be processed with this url.
Well I'm not sure what root is doing in there, have you tried
RewriteRule ^root/([a-z]{2})/(.*)$ root/$2.php?lang=$1
What's wrong is the second RewriteCond. But your question is vague. If you mean you're trying to convert
root/foo/sub/.../something.php
to
root/sub/.../something.php?lang=foo
then start fidgeting with this:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^root/([^/]+)/sub/(.*)/%{REQUEST_FILENAME}$ root/sub/$2/%{REQUEST_FILENAME}?lang=$1