I have an Apache2 RewriteCond directive that redirects all URL's to a subdirectory on my site, except for a long string of exceptions.
The rule goes as follows: (the long string has been replaced with foo|bar|baz)
RewriteCond %{REQUEST_URI} !^/(foo|bar|baz) [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /subdir/$1 [QSA,L]
The regex matches all paths that do not start with /foo, /bar, /baz and redirects them to /subdir/path/request.
For example:
/foo/page => /foo/page
/bar/page => /bar/page
...
/not_foo/page => /subdir/not_foo/page
/not_bar/page => /subdir/not_bar/page
...
What I want to do is to programmatically get this list of exceptions, to reduce code copying (probably with PHP's getEnv('VAR')). I've tried a lot of different possible options, but I just can't get it to work.
My idea is to use a RewriteRule with [E=ENV:values] but I just can't wrap my mind around how to accomplish this.
Thanks for the help!
How about file_get_contents('.htaccess');? You can parse the file from there to get what you are looking for.
Related
I have two rewrite rules for my application:
The first rule is a rule for /chef/index.php:
/chef/name -> /chef/?id=1234
The second rule is a rule for /recipes/index.php:
/r/name/nice-name-for-recipe ->
/recipes/?id=1234&nice_name=nice-name-for-recipe-name
The two rules work separately, but if I enabled both of them:
the first chef rule does not work,
the second recipes rule seems to work.
I tried to swap the order of the rules but I still cannot make both of them work.
Rules:
RewriteEngine On
RewriteRule ^([a-zA-Z0-9_-]+)/chef/$ $1 [QSA]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /chef/index.php?id=$1 [QSA]
RewriteRule ^([a-zA-Z0-9_-]+)/r/$ $1/$2 [QSA]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /recipes/index.php?id=$1&nice_name=$2 [QSA]
In the first rule, you tried to match ^([a-zA-Z0-9_-]+)/chef/$.
/chef/$ means it matches a url that ends with /chef/,
because $ means the end of string, e.g.:
http://anything.dev/chef/
So it does not match /chef/name/, it matches /chef/.
Similarly, your second rule does not match /r/name/nice-name-for-recipe,
it matches /r/$.
These rules just tell apache to fallback to static files.
It is useful if you need to serve static files,
but they are unrelated to this question.
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
So your rules are roughly just:
RewriteRule ^(.*)$ /chef/index.php?id=$1 [QSA]
RewriteRule ^(.*)$ /recipes/index.php?id=$1&nice_name=$2 [QSA]
Now it is clear that why these rules work separately but only the second one works if you put them together.
They both matches ^(.*)$, that is every url (except those urls for static files).
Thus when putting them together, only the second one wins.
So the real rule effect is:
/chef/name ->
/chef/index.php?id=/chef/name&nice_name=
/r/name/nice-name-for-recipe ->
/chef/index.php?id=/r/name/nice-name-for-recipe&nice_name=
P.S. I think the deep causes of this question are:
You try to write regular expressions without understanding them.
The regular expression syntax is hard to understand. Specifically, $ is both used as pattern and variable prefix.
The index.php code is dirty. It should not accept urls blindly. If index.php errors out, the two rules will not seem to work. Dirty code is hard for detecting and locating problems, and insecure (attackers can construct dangerous special urls).
Your IDE is not smart enough to warn you against RewriteRule ^(.*)$ /recipes/index.php?id=$1&nice_name=$2 [QSA] since $2 is unset.
You're writing that you want to transform dev.website.com/chef/name into dev.website.com/chef/?id=1234. This cannot work as the id in the target URL doesn't exist in the source URL, so you need to think about how you want to include the ID in the original URL as well.
Once you sorted that out, I'd recommend you to read up more on regular expressions to fix the mod_rewrite rules.
Here's a great resource for testing and explaining regular expressions: https://regex101.com/
I have a URL i.e "www.mysite.com". I want to send parameters via url in following ways:
www.mysite.com/count
www.mysite.com/search_caption?query=huha
www.mysite.com/page=1
www.mysite.com/search_caption?query=huha&page=1
In each of these cases I want to load index.php page with parameters as follows for each case:
var_dump($_REQUEST) results into [count]
var_dump($_REQUEST) results into [query="huha"]
var_dump($_REQUEST) results into [page=1]
var_dump($_REQUEST) results into [query="huha",page=1]
How do I write .htaccess file to achieve this?
I am using this code but it is capturing only params after "?" and not everything after first slash
Options +FollowSymLinks
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#RewriteRule ^([^/]+)/?$ index.php?{REQUEST_FILENAME}=$1 [L,QSA]
RewriteRule .* /index.php [L]
Something like that should get close, though you really should think about those strange URL patterns instead of trying to fix them afterwards with rewriting...
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L,QSA]
RewriteRule ^count index.php?count=1 [L]
RewriteRule ^page/(.*)$ index.php?page=1 [L]
RewriteRule ^ index.php [L,QSA]
Some notes:
the first three RewriteRules are exceptions necessary because your given requests do not follow a sane and common pattern. They appear somewhat chaotically chosen.
this certainly is not free of issues, I did not test it, only typed it down.
this assumes the "page" example to be requested like as discussed in the comments.
index.php actually has to exist as a file, otherwise this will result in a rewrite loop
Given all that these rewritings should happen:
www.mysite.com/count => index.php?count=1
www.mysite.com/search_caption?query=huha => index.php?query=huha
www.mysite.com/page/1 => index.php?page=1
www.mysite.com/search_caption?query=huha&page=1 => index.php?query=huha&page=1
Also note that the rules above are written for .htaccess style files. To be used as normal rules, so inside the http servers host configuration, they would have to be written slightly different. You should only use .htaccess style files if you really, really have to, so if you have no access to the configuration files. You should always try to avoid those files if somehow possible. They are notoriously error prone, hard to setup and debug and really slow the server down. So if you have access to the http server configuration, then defines such rules in there instead.
I am implementing a caching system for dynamically generated images. The script is called via /img/type/param1/param2.png, where multiple types are possible and the number of parameters are variable. The script generates the image and dumps it to disk.
I would like .htaccess rules, which when requesting the image generation script:
checks to see if a cached file exists
if so, mod_rewrite to the cached file
if not, don't do anything so that the script runs
What I have so far is a slightly modified logic:
mod_rewrite to where the cached file would be
check to see if the file exists
if not, mod_rewrite the request back again
If there's a better way to do this, I'd love to hear it. In any case, the relevant part of my .htaccess file:
RewriteRule ^img/(type1|type2)/(.*)$ /images/cache/$1/$2
RewriteCond /^(.*)$ !-f
RewriteRule images/cache/(.*) /img/$1
This doesn't seem to work quite right. To debug it, I inserted the following after the first RewriteRule (the target page just dumps its input):
RewriteRule ^(.*)$ /htaccess.php?url=$1 [END]
As a result, I get:
Array
(
[url] => /images/cache/a/l/300.png/a/l/300.png
)
I don't understand why $2 contains l/300.png/a/l/300.png instead of just l/300.png.
If I change first RewriteRule to include an [L], I get the expected result. However, I experience the exact same "double-matching" issue on the second RewriteRule which reverts the request back into the non-cache version. However, adding [L] to this second RewriteRule:
RewriteRule ^img/(type1|type2)/(.*)$ /images/cache/$1/$2 [L]
RewriteCond /^(.*)$ !-f
RewriteRule images/cache/(.*) /img/$1 [L]
yields an Internal Server Error.
What am I doing wrong, and how do I fix this issue?
The first logic that you have is what you want to be doing, that bypasses the possible looping issues. You just need to extract the relevant bits from the %{REQUEST_URI} then backreference them using % references. So for example:
RewriteCond %{REQUEST_URI} ^/img/(.*)$
RewriteCond %{DOCUMENT_ROOT}/images/cache/%1 -f
RewriteRule ^img/(.*)$ /images/cache/$1 [L]
or using the regex patterns that you had:
RewriteCond %{REQUEST_URI} ^/img/(type1|type2)/(.*)$
RewriteCond %{DOCUMENT_ROOT}/images/cache/%1/%2 -f
RewriteRule ^img/(.*)$ /images/cache/$1 [L]
i store uploaded files at /storage/ this way
public-adam-luki-uploads-123783.jpg
park-hanna-adel-propic-uploads-787689.jpg
the '-' count unknown because it slice the pic description
i want my users to be able to access it as
http://site.com/public/adam/luki/uploads/123783.jpg
http://site.com/park/hanna/adel/propic/uploads/787689.jpg
i think it is the same problem here
mod_rewrite with an unknown number of variables
but i can't do it because i'm new to mod_rewrite module
i hope you can help me guys with the right rewriterule
The question you link to doesn't actually do what you are trying to do (although the principle is the same) what they do is convert the url to GET variables.
If all you want to do is convert / to - then you can use a simple rewrite rule that will run in a loop:
ReWriteRule ^(.*)/(.*)$ $1-$2 [L]
There are of course a few caveats to that...
Firstly, even if you are trying to get to a real directory/file the rule will still switch out / and - and leave you with a 404. You can get around that by adding conditions; to stop it rewriting real files:
RewriteCond %{REQUEST_FILENAME} !-f
You would do better however to limit the matches to only images (jpgs):
RewriteCond %{REQUEST_FILENAME} !-f
ReWriteRule ^(.*)/(.*)\.jpg$ $1-$2.jpg [L]
Preferred Solution
ReWriteCond %{REQUEST_FILENAME} !-f
ReWriteRule ^images/(.*)/(.*)uploads[-/](\d+)\.jpg$ images/$1-$2uploads-$3.jpg [L]
RewriteCond %{REQUEST_FILENAME} !-f
ReWriteRule ^images/(.*)$ storage/$1 [L]
This solution requires you to use urls like:
http://site.com/images/park/hanna/adel/propic/uploads/787689.jpg
The pseudo directory images means you can be sure that the url is actually one that you want to redirect and it doesn't break other images/links on your site.
The above rules take a url (like the example above) and transforms it like so:
images/park/hanna/adel/propic/uploads/787689.jpg <--- Original
images/park-hanna/adel/propic/uploads-787689.jpg
images/park-hanna-adel/propic/uploads-787689.jpg
images/park-hanna-adel-propic/uploads-787689.jpg
images/park-hanna-adel-propic-uploads-787689.jpg
storage/park-hanna-adel-propic-uploads-787689.jpg <--- Final
I need to be able to shorten my page from:
mydomain.com/mixtape.php?mixid=(WHATEVER NUMBER)
To:
mydomain.com/m/(WHATEVER NUMBER)
Now usually this wouldn't be much of an issue for me to figure out, but becasue of a few pre-existing functions in my .htaccess file, it is really hard for this function not to improperly interact with the others.
Below is the current code of my .htaccess file (AND NONE OF IT CAN CHANGE)
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/\.]+)/?$ profile.php?user=$1 [L]
Above, the .htaccess file is shorting my
mydomain.com/profile.php?username=(USERNAME)
to
mydomain.com/(USERNAME)
Is there anyone out there than can help me by being able to shorten the m/index.php?mixid and not have it conflict with the pre-existing function?
Prepend this rule to your .htaccess block rewriting the profile url (after turning the rewrite engine on) :
RewriteCond $1 ^m/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ mix.php?id=$1 [L]
That rule will now only be used for URLS like :
mydomain.com/m/(WHATEVER NUMBER)
The first line is a condition that the incoming URL must start with m/
The second and third lines are conditions that the incoming URL does not represent an actual file or folder (we wouldn't want our humble rewrite rule to block us from a real resource).
The fourth line is the actual rule itself witch uses a regular expression syntax to match and capture everything that appears after host name and passes it to the mixtape.php file as a GET parameter called id. This line also contains the [L] flag which states that no more rules or rewriting will occur on the current incoming URL.
In your mix.php file you can use the explode method to split the resulting string into an array :
http://example.com/m/foo/bar =>
`http://example.com/mixtape.php?id=/m/foo/bar
$splitArr = explode('/',$_GET['id']);
$splitArr =>
array (
0 => 'm',
1 => 'foo',
1 => 'bar',
)
and remove the initial m with
array_shift();
Then you are left with $splitArr containing all the parts of your URL, split with a / (slash) delimiter.
The URL example.com/m/foo/bar would look like :
array (
0 => 'foo',
1 => 'bar',
)
It is important to place this rule before the existing one as the existing rule will act on any incoming URL. The final two rules that you have should appear like this :
RewriteEngine on
RewriteCond $1 ^m/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ mix.php?id=$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/\.]+)/?$ profile.php?user=$1 [L]
Regarding your statement :
AND NONE OF IT CAN CHANGE
I would seriously recommend that you consider implementing a small change on that first rule. Making the final url something like mydomain.com/users/(USERNAME) (as they do here). In these cases it is much better to be more specific than overly general (as the current rule is). Have you considered the confusion that could be created if someone was to chose a user name such as :
about
faq
home
While perfectly valid usernames these users profiles would be :
mydomain.com/about
mydomain.com/faq
mydomain.com/home
Those usernames will block important URLs that you might want to save for other locations on your site. I think it is clear why those user names would be undesirable.
RewriteRule ^m/([0-9]+)$ /mixtape.php?mixid=$1
Put in before or after the existing rule. Should not cause any conflict.