Struggling with mod_rewrite [duplicate] - mod-rewrite

I hope I'm missing something silly.
I'm trying to redirect URLs using .htaccess on Apache 2.2 using the PHP 5.4 cartridge on OpenShift's free hosting service.
This matches the URI /permalink/a123 (note the lack of leading slash in the rule's filter pattern):
RewriteEngine On
RewriteRule permalink/a.*$ /permalink/b [R=301,L]
This does not match the URI /permalink/a123 (note the leading slash in the rule's filter pattern):
RewriteEngine On
RewriteRule /permalink/a.*$ /permalink/b [R=301,L]
So what stupid thing do I have wrong?
Thanks.

The URI used to match patterns in a RewriteRule are canonicalized in a per-directory context (either in an htaccess file or in a <Directory> container) by removing the leading /. So if the requested URL is:
http://example.com/web/permalink/123
And from within an htaccess file in the document root, the URI used to match rules is web/permalink/123. But within an htaccess file in the web folder, the URI is permalink/123, etc.
Thus you can't have your patterns start with a / because they're stripped from the URI in the context of an htaccess file.

Related

Apache2 rewrite returns filename extension

I really don't understand where I'm doing wrong. I'm trying to apply a rule to
http://localhost/prezzo/account/1
so that it is rewritten as
http://localhost/prezzo/account/test.php?user=1
I'm using UniformServer as WAMP. I placed the .htaccess file in the subfolder I'm working on (prezzo/account/) with the following rule:
RewriteEngine On
RewriteRule (\w+)/?$ test.php?user=$1 [L]
htaccess tester reports that the rule is applied correclty.
But when I go to the URL http://localhost/prezzo/account/1 and test.php is loaded - which contains simply
<?php
echo $_GET['user'];
?>
it returns the string "php" instead of "1".
If I try with
RewriteRule ^prezzo/account/(\w+)/?$ prezzo/account/test.php?user=$1 [L]
I get 404 not found although htaccess tester reports that the rule is applied correctly and the URL is rewritten as
http://localhost/prezzo/account/test.php?user=1
that if I copy/paste in the address bar it works.
But when I go to the URL http://localhost/prezzo/account/1 and test.php is loaded [...] it returns the string "php" instead of "1".
Yes, this is expected with the rule as posted.
This appears to work in the "htaccess tester" because that tool only makes a single pass through the file, which is not how a real server works.
RewriteRule (\w+)/?$ test.php?user=$1 [L]
When you request /prezzo/account/1 then...
The request is rewritten to test.php?user=1
The L flag causes the rewrite engine to start over using the rewritten URL (test.php?user=1) as input to the next round of processing.
The request is rewritten to test.php?user=php since the regex (\w+)/?$ captures the php part of test.php. (The \w shorthand character class excludes dots and the regex is not anchored.)
The L flag causes the rewrite engine to start over using the rewritten URL (test.php?user=php) as input to the next round of processing.
The request is rewritten to test.php?user=php (again).
Since the URL has passed through unchanged the rewriting process stops and the request is finally rewritten to /test.php?user=php.
Solution A - Use the END flag
One solution is to simply use the END flag (Apache 2.4) instead of L to prevent the rewriting engine from "looping". It will stop as soon as the directive is processed. For example:
RewriteRule (\w+)/?$ test.php?user=$1 [END]
Solution B - Make regex more specific
The other solution (or as well as) is to make the regex more specific, so that it doesn't match test.php. ie. Only match the URL format you are expecting.
The regex (\w+)/?$ would seem to be too generic, as it is basically just matching the last group of letters/numbers on the URL-path. If you only want to match digits (a "user-id") then you could make the regex more restrictive and match only digits instead.
You should also anchor the regex at the start, so that it matches a whole path segment, rather than just capturing the last part that matches. In fact, simply anchoring the above regex would have also resolved this, since test.php would have failed to match because \w does not match dots.
For example:
RewriteRule ^(\d+)/?$ test.php?user=$1 [L]
This will match digits only in the last path segment.
If I try with
RewriteRule ^prezzo/account/(\w+)/?$ prezzo/account/test.php?user=$1 [L]
I get 404 not found although htaccess tester reports that the rule is
applied correctly and the URL is rewritten
If the .htaccess file is located in the /prezzo/account subdirectory (as you stated) then the above will never match and the directive does nothing.
That testing tool assumes the .htaccess file is located in the document root only. If your .htaccess was located in the document root and not the subdirectory, then that directive would indeed be OK.
In a directory context (eg. .htaccess) the RewriteRule pattern matches against the URL-path relative to the directory that contains the .htaccess file.
Aside:
In your link, the entire file would seem to be:
RewriteEngine On
#RewriteCond %{REQUEST_FILENAME} !-f
#RewriteCond %{REQUEST_FILENAME} !-d
#RewriteCond %{REQUEST_FILENAME} !-l
#RewriteRule . index.php [L]
RewriteRule (\w+)/?$ test.php?user=$1 [L]
The first rule is commented out so does not apply here. However, if you uncomment that first rule then the rules are in the wrong order. Since a request for /prezzo/account/1 would first be rewritten to index.php and you'd have the same problem as before.
The order of rules is important.

Using mod_rewrite to redirect all pages in folder on old domain to index of new domain

I'm trying to redirect all pages on an old site (wellnowwhat.net/nin/*.*) to the index of a new site (sykonaut.net/nin_old/). I only have access to the new site's .htaccess (the old site is owned by a friend), so I'm testing going the opposite direction. I can redirect the root (sykonaut.net) to his root (wellnowwhat.net) using mod_rewrite, but I cannot redirect my nin_old directory to his nin directory. I'm guessing I wouldn't be able to redirect the opposite direction, either. Here is [the entirety of] the code in my .htaccess:
AddHandler php5_2-wrap .php
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www.sykonaut.net$ [NC]
RewriteRule ^/nin_old(.*)$ http://www.wellnowwhat.net/nin [R,NC,L]
It does not work. Anyone know why?
Place your .htaccess in the nin_old directory.
RewriteEngine on
RewriteBase /nin_old
RewriteRule ^.* http://www.wellnowwhat.net/nin/ [R,NC,L]
When you redirect to an external URL to a directory, I would add the trailing slash as this reduce an extra redirection.
Also when you test everything and it's all right, change R to R=301 as this is preferred by most search engine for Permanent Redirection.
In apache 2.0 and higher, the prefix (leading "/") is removed from the URI before it's put through the rewrite engine for rules in the .htaccess file. Try removing the leading slash from your regular expression in your rule:
RewriteRule ^nin_old(.*)$ http://www.wellnowwhat.net/nin [R,NC,L]

Need help removing part of filename from url with mod_rewrite

I'm trying to reformat my url to be a bit shorter. Right now the links end up as this: website.com/image?id=name.jpg
What I want to have the link come out as is m.website.com/name, without the file exension or image.php file in the url. I figure mod_rewrite is the way to do it, so any help will be greatly appreciated.
In order to make it so someone accessing the URL http://m.website.com/name gets served the content for http://website.com/image?id=name.jpg, you first need to check the hostname for m.website.com, then match the name part of the URI. Using that match, you can proxy the request (using a [P]) or, if both website.com and m.website.com are hosted on the same server, just simply internally rewrite. Try putting this in your .htaccess file in your document root:
RewriteEngine on
# check the host (NC = no case)
RewriteCond %{HTTP_HOST} ^m\.website\.com$ [NC]
# don't rewrite /image
RewriteCond %{REQUEST_URI} !^/image
# Match the first non-slash word and rewrite
RewriteRule ^([^/]+)$ /image?id=$1 [L]
This will rewrite http://m.website.com/name to /image?id=name.jpg, but it will not rewrite http://m.website.com/path/name. If you want paths (and everything else) to be included in the id parameter, change the ([^/]+) to (.*) in the RewriteRule.

rewriting URLs from .json to .php

I want http://server/path/app.json?a=foo&b=bar to map to http://server/path/foo.php?a=foo&b=bar using mod_rewrite. I have the following incantation in my .htaccess which doesn't give any joy
RewriteEngine On
RewriteRule ^([^.?]+).json(.*)$ $1.php$2 [L]
Suggestions?
Update: Adding rewrite.log and error.log output (comments don't allow formatting)
I get the following in the rewrite.log
strip per-dir prefix: /Users/user/Sites/dir/app.json -> app.json
applying pattern '^([^.?]+).json(.*)$' to uri 'app.json'
rewrite 'app.json' -> 'app.php'
add per-dir prefix: app.php -> /Users/user/Sites/dir/app.php
internal redirect with /Users/user/Sites/dir/app.php [INTERNAL REDIRECT]
and the apache server log says
The requested URL /Users/user/Sites/dir/app.php was not found on this server.
If I read your question correctly you want:
http://server/path/app.json?a=foo&b=bar
Going to:
http://server/path/foo.php?a=foo&b=bar
Sowhen you capture (app).json $1 is app and $2 is your second parenthesis, it's ... nothing (the part between json and the ?). As everything after the question mark is the QUERY STRING and cannot be captured here. Your rewriteRule is working on the requested file, not on the QUERY STRING. So you didn't captured foo anywhere. For the QUERY_STRING you could use the [QSA] flag on the rewriteRule, that would simply append a=foo&b=bar after your rewrite.
RewriteEngine On
RewriteRule ^([^.?]+).json(.*)$ $1.php$2 [L]
Here you tell apache to reuse $1 (the filename without .json), so app.json will get redirected to app.php, not foo.php.
RewriteEngine On
RewriteRule ^([^.?]+).json(.*)$ $1.php [L,QSA]
Will redirect app.json?a=b&z=r to app.php?a=b&z=r.
Now if you really need to capture foo as the first QUERY_STRING parameter the rule will become harder. But you could do it like that (here instead of the first parameter I detect the parameter 'a=' and capture his value in %4):
RewriteCond %{QUERY_STRING} (.*)(^|&|%26|%20)a(=|%3D)([^&]+)(.*)$
RewriteRule ^([^.?]+).json$ %4.php? [L,QSA]

mod_rewrite issues

I have used the mod_rewrite module but was not able to redirect to the target page - I am getting an error:
The requested URL /old.html was not found on this server.
Rewrite rules as follows:
RewriteEngine On
RewriteRule ^/IN/index.html$ /IN/index.iface [L]
You need to request a URL with a path that’s matched by the pattern of your RewriteRule directive. So in your case obviously /IN/index.html (where the . can actually be any character, as it’s not escaped).

Resources