mod_rewrite: why can't environment variables be used to prevent recursion? - mod-rewrite

Why can't I use mod_rewrite rules similar to this:
RewriteEngine On
RewriteCond %{ENV:did_rewrite} !=true
RewriteCond %{REQUEST_URI} ^(.*)/
RewriteRule (.*) %1/foo.php?original=$1 [E=did_rewrite:true]
To prevent recursion?
When I turn up the RewriteLogLevel, I see:
[.../initial] (3) [perdir /.../test/] strip per-dir prefix: /.../test/stuff -> stuff
[.../initial] (3) [perdir /.../test/] applying pattern '(.*)' to uri 'stuff'
[.../initial] (4) [perdir /.../test/] RewriteCond: input='' pattern='!=true' => matched
[.../initial] (4) [perdir /.../test/] RewriteCond: input='/test/stuff' pattern='(.*)/' => matched
[.../initial] (2) [perdir /.../test/] rewrite 'stuff' -> '/test/foo.php?original=stuff'
[.../initial] (5) setting env variable 'did_rewrite' to 'true'
[.../initial] (3) split uri=/test/foo.php?original=stuff -> uri=/test/foo.php, args=original=stuff
[.../initial] (1) [perdir /.../test/] internal redirect with /test/foo.php [INTERNAL REDIRECT]
[.../initial/redir#1] (3) [perdir /.../test/] strip per-dir prefix: /.../test/foo.php -> foo.php
[.../initial/redir#1] (3) [perdir /.../test/] applying pattern '(.*)' to uri 'foo.php'
[.../initial/redir#1] (4) [perdir /.../test/] RewriteCond: input='' pattern='!=true' => matched
[.../initial/redir#1] (4) [perdir /.../test/] RewriteCond: input='/test/foo.php' pattern='(.*)/' => matched
[.../initial/redir#1] (2) [perdir /.../test/] rewrite 'foo.php' -> '/test/foo.php?original=foo.php'
...
It seems like, on the "recursive" call, the environment variable somehow becomes un-set… But I can't figure out why that would happen.
To be clear: I know there are a bunch of ways to prevent this kind of recursion. I'd like to understand why this particular way doesn't work.

I think this blog entry may have your answer. In summary, when Apache performs an internal redirect, it renames all of the environment variables:
static apr_table_t *rename_original_env(apr_pool_t *p, apr_table_t *t)
{
const apr_array_header_t *env_arr = apr_table_elts(t);
const apr_table_entry_t *elts = (const apr_table_entry_t *) env_arr->elts;
apr_table_t *new = apr_table_make(p, env_arr->nalloc);
int i;
for (i = 0; i < env_arr->nelts; ++i) {
if (!elts[i].key)
continue;
apr_table_setn(new, apr_pstrcat(p, "REDIRECT_", elts[i].key, NULL),
elts[i].val);
}
return new;
}
Unfortunately, this doesn't seem to be documented anywhere.

Related

APIs Route in Laravel

I have a route like this in api.php:
...
Route::resource("infos",PaymentController::class)->only([
'show','store'
]);
...
And when I call my API like these:
/api/infos/ABC123 => success with Status Code: 200 (in access.log)
/api/infos/ABC123/ => there area 2 log (Status code: 301; then Status code 200)
/api/infos/ABC123//// => there area 2 log (Status code: 301; then Status code 200)
Why when I add slash symbols, there are 2 line in access.log?
Thanks!
The .htaccess file that ships with Laravel has a section in it that strips trailing slashes by using a redirect.
This is from the .htaccess file, which can be found here:
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
So, the first line you're seeing is the redirect triggered by this .htaccess code, and the second line is the final request without the slash.
Because when you add / then Laravel redirect it to address without /.

Nginx - URL rewrite rules

I have for example this URL:
www.example.com/folder1/folder2/edit.php?username=nickname
Actually I have this rewrite rules:
location / {
root /var/www;
index index.php index.html index.htm;
# First rewrite rule output: www.example.com/nickname
rewrite ^/([A-Za-z0-9_]+)$ /folder1/folder2/user.php?username=$1;
# Second rewrite rule output: www.example.com/nickname/edit
rewrite ^/([A-Za-z]+)/edit$ /folder1/folder2/edit.php?username=$1;
}
However, that works fine.
But I need to rewrite my URL like this:
www.example.com/nickname/edit/info
It has a couple of parameters:
www.example.com/folder1/folder2/edit.php?username=nickname&info=basic
I tried everything but with no success:
location / {
root /var/www;
index index.php index.html index.htm;
# First rewrite rule output: www.example.com/nickname
rewrite ^/([A-Za-z0-9_]+)$ /folder1/folder2/user.php?username=$1;
# Second rewrite rule output: www.example.com/nickname/edit
rewrite ^/([A-Za-z]+)/edit$ /folder1/folder2/edit.php?username=$1;
# Here where I'm stuck
# www.example.com/nickname/edit/info
rewrite ^/edit/([A-Za-z]+)/info$ /folder1/folder2/edit.php?user=$1&info=$2;
}
Your last attempt seems to have mixed up the location of edit. Also, nothing is passed to info as there is only one capturing group, perhaps
rewrite ^/([A-Za-z]+)/edit/([A-Za-z]+)$ /folder1/folder2/edit.php?username=$1&info=$2;
will work instead?

mod_rewrite 301 redirect hundreds of old unstructured url

I need to redirect (301) hundreds of old unstructured urls to new ones. Old urls have querystring domain/directories/randomtext?args=1
I have a file with oldurl newurl lines.
I am trying with RewriteMap in virtualhost
RewriteMap mapfile txt:mapfile.txt
RewriteCond ${mapfile:$1|NOT_FOUND} !NOT_FOUND
RewriteRule ^(.+)$ ${mapfile:$1} [L,R=301]
Is it possible to include Querystring in RewriteCond?
I solved this way:
#define map file with oldurl newurl format
RewriteMap mapfile txt:mapfile.txt
#test if request_uri?query_string is in mapfile so length is greater than ""
RewriteCond ${mapfile:$1?%{QUERY_STRING}} >""
#If matched rewrite oldurl to mapfile[key]
RewriteRule ^/(.+)$ ${mapfile:$1?%{QUERY_STRING}}? [R=301]
I think this could be improved because cache lookup always fails. I suppose only request_uri is cached.
RewriteLog
(2) init rewrite engine with requested uri /prueba
(3) applying pattern '(.*)' to uri '/prueba'
(4) RewriteCond: input='/prueba' pattern='^/$' => not-matched
(3) applying pattern '(.*)' to uri '/prueba'
(4) RewriteCond: input='/prueba' pattern='^/index.php$' => not-matched
(3) applying pattern '^/(.+)$' to uri '/prueba'
(4) RewriteCond: input='/prueba' pattern='!-f' => matched
(4) RewriteCond: input='/prueba' pattern='!-d' => matched
(4) RewriteCond: input='/prueba' pattern='!.*\.(ico|gif|jpg|jpeg|png|js|css|GIF|JPG)' => matched
(6) cache lookup FAILED, forcing new map lookup
(5) map lookup OK: map=mapfile[txt] key=prueba?prueba=1 -> val=/index.php
(4) RewriteCond: input='/index.php' pattern='>""' => matched
(5) cache lookup OK: map=mapfile[txt] key=prueba?prueba=1 -> val=/index.php
(2) rewrite '/prueba' -> '/index.php?'

Help with mod_rewrite

I'm trying to get mod_rewrite working with the following URLs:
/events.php?view=details&id=$var
/events.php?view=edit&id=$var
Obviously my goal is to have /events/details/$var and /events/edit/$var be my actual URL's, and $var is an unique ID.
My .htaccess file
RewriteEngine On
# redirect 301 /events.php http://www.google.com
# If the rule above is active, it does redirect to google.com,
# so .htaccess is working
RewriteRule ^events/([^/]*)/([^/]*)\$ /events.php?view=$1&id=$2 [L]
Currently when I go to /events/details/$var it's displaying /events.php but not picking up the variables being passed in.
Any help would be appreciated!
Update: I removed the .php mentioned by OverZealous. /events/details/$var still displays /events.
// From events.php
echo $_REQUEST['view']; //returns nothing
Update2:
I enabled the mod_rewrite log (level 5) and got the following output: (I stripped out the IP, date, my domain details etc)
[sid#7fc6d76f5608][rid#7fc6d79ad908/subreq] (3) [perdir /var/www/webroot/] add path info postfix: /var/www/webroot/events.php -> /var/www/webroot/events.php/details/35
[sid#7fc6d76f5608][rid#7fc6d79ad908/subreq] (3) [perdir /var/www/webroot/] strip per-dir prefix: /var/www/webroot/events.php/details/35 -> events.php/details/35
[sid#7fc6d76f5608][rid#7fc6d79ad908/subreq] (3) [perdir /var/www/webroot/] applying pattern '^events/([^/]*)/([^/]*)$' to uri 'events.php/details/35'
[sid#7fc6d76f5608][rid#7fc6d79ad908/subreq] (1) [perdir /var/www/webroot/] pass through /var/www/webroot/events.php
[sid#7fc6d76f5608][rid#7fc6d79a88e8/initial] (3) [perdir /var/www/webroot/] add path info postfix: /var/www/webroot/events.php -> /var/www/webroot/events.php/details/35
[sid#7fc6d76f5608][rid#7fc6d79a88e8/initial] (3) [perdir /var/www/webroot/] strip per-dir prefix: /var/www/webroot/events.php/details/35 -> events.php/details/35
[sid#7fc6d76f5608][rid#7fc6d79a88e8/initial] (3) [perdir /var/www/webroot/] applying pattern '^events/([^/]*)/([^/]*)$' to uri 'events.php/details/35'
[sid#7fc6d76f5608][rid#7fc6d79a88e8/initial] (1) [perdir /var/www/webroot/] pass through /var/www/webroot/events.php
[sid#7fc6d76f5608][rid#7fc6d7a597c8/subreq] (3) [perdir /var/www/webroot/] add path info postfix: /var/www/webroot/details -> /var/www/webroot/details/35
[sid#7fc6d76f5608][rid#7fc6d7a597c8/subreq] (3) [perdir /var/www/webroot/] strip per-dir prefix: /var/www/webroot/details/35 -> details/35
[sid#7fc6d76f5608][rid#7fc6d7a597c8/subreq] (3) [perdir /var/www/webroot/] applying pattern '^events/([^/]*)/([^/]*)$' to uri 'details/35'
[sid#7fc6d76f5608][rid#7fc6d7a597c8/subreq] (1) [perdir /var/www/webroot/] pass through /var/www/webroot/details
Why do you have \.php at the end? Do you want the URLs to be /events/details/123.php? Because that's not what your example is.
I think you want your rewrite rule to look like:
RewriteEngine On
RewriteRule ^events/([^/]*)/([^/]*)$ /events.php?view=$1&id=$2 [L]

mod_rewrite and mod_dir collision

I'm edditing my htaccess to internally redirects
just about any URL to a php page handler:
RewriteRule ^images\/ - [L,NS]
RewriteRule ^docs\/ - [L,NS]
RewriteRule ^([A-Za-z0-9\_\-]+)\/?$ pages/pagehandler.php?page=$1 [L,QSA,NS]
this works fine accept that directories that are enteres in the address
bar with no trailing slash for some reason get duplicate query strings,
and for some reason the address bar of the browser changes
for example, if I type the URL:
localhost/movies
if there's a directory called movies in the site root
than the address changes to:
localhost/movies/?page=movies
I guess this is some collision with mod_dir but I don't know
how to overcome it, if I use:
<IfModule mod_dir.c>
DirectorySlash Off
</IfModule>
Than it works, but I don't want this, I think for some reason the url
is rewritten than mod dir changes it and than it is rewritten again thus
making duplicate query strings,
Any Ideas?
EDIT: I Add a relevant part of the Rewritelog, this is all from one request:
strip per-dir prefix: /opt/lampp/htdocs/movies -> movies
applying pattern '^images\/' to uri 'movies'
strip per-dir prefix: /opt/lampp/htdocs/movies -> movies
applying pattern '^docs\/' to uri 'movies'
strip per-dir prefix: /opt/lampp/htdocs/movies -> movies
applying pattern '^pages\/' to uri 'movies'
strip per-dir prefix: /opt/lampp/htdocs/movies -> movies
applying pattern '^([A-Za-z0-9\_\-]+)\/?$' to uri 'movies'
rewrite 'movies' -> 'pages/pagehandler.php?page=movies'
split uri=pages/pagehandler.php?page=movies -> uri=pages/pagehandler.php, args=page=movies
add per-dir prefix: pages/pagehandler.php -> /opt/lampp/htdocs/pages/pagehandler.php
trying to replace prefix /opt/lampp/htdocs/ with /
strip matching prefix: /opt/lampp/htdocs/pages/pagehandler.php -> pages/pagehandler.php
add subst prefix: pages/pagehandler.php -> /pages/pagehandler.php
internal redirect with /pages/pagehandler.php [INTERNAL REDIRECT]
strip per-dir prefix: /opt/lampp/htdocs/movies/ -> movies/
applying pattern '^images\/' to uri 'movies/'
strip per-dir prefix: /opt/lampp/htdocs/movies/ -> movies/
applying pattern '^docs\/' to uri 'movies/'
strip per-dir prefix: /opt/lampp/htdocs/movies/ -> movies/
applying pattern '^pages\/' to uri 'movies/'
strip per-dir prefix: /opt/lampp/htdocs/movies/ -> movies/
applying pattern '^([A-Za-z0-9\_\-]+)\/?$' to uri 'movies/'
rewrite 'movies/' -> 'pages/pagehandler.php?page=movies'
split uri=pages/pagehandler.php?page=movies -> uri=pages/pagehandler.php, args=page=movies&page=movies
add per-dir prefix: pages/pagehandler.php -> /opt/lampp/htdocs/pages/pagehandler.php
trying to replace prefix /opt/lampp/htdocs/ with /
strip matching prefix: /opt/lampp/htdocs/pages/pagehandler.php -> pages/pagehandler.php
add subst prefix: pages/pagehandler.php -> /pages/pagehandler.php
internal redirect with /pages/pagehandler.php [INTERNAL REDIRECT]
Also the relevant part from the access log:
"GET /movies HTTP/1.1" 301
"GET /movies/?page=movies HTTP/1.1" 200
Can you change your rewrite rule to:
RewriteRule ^([A-Za-z0-9\_\-]+)/?$ /pages/pagehandler.php?page=$1 [L,QSA,NS]
Note the / before pages.
For anyone having this problem, what I did ultimately in disable the Directory Slash:
<IfModule mod_dir.c>
DirectorySlash Off
</IfModule>
And I used a RewriteRule to 301 Redirect all directory requests than do not end with a /
I guess this is some sort of collision between mod_rewrite and mod_dir
as I primarily thought
And I used a RewriteRule to 301 Redirect all directory requests than do not end with a /
I had a similar problem but could not find a solution over mod_rewrite (did not know how to add this slash with a 301 redirect - tried everything).
What I did is to define:
<IfModule mod_dir.c>
DirectorySlash Off
</IfModule>
ErrorDocument 404 404.php
If now the trailing slash is missed, 404.php is used redirecting the client if the url has no trailing slash now:
<?php
$home="http://".$_SERVER['HTTP_HOST'];
$url=$home.$_SERVER['REQUEST_URI'];
?><html>
<head>
<title><?php echo $_SERVER['REDIRECT_STATUS']; ?></title>
<?php if(!preg_match('/^.+\/$/', $url)) { ?>
<meta http-equiv="refresh" content="0; URL=<?php echo $url; ?>/">
<? } ?>
</head>
<body>
<h1><?php echo $_SERVER['REDIRECT_STATUS']; ?></h1>
</body>
</html>
It smells like hell but worksforme ...

Resources