.htaccess rewrite rule handled correctly - .htaccess

I want to redirect rewrite /zp-core/admin.php to admin but somehow I can't get it to work.
I've added this rule to my htaccess:
RewriteEngine On
RewriteBase /
RewriteRule ^/admin$ /zp-core/admin.php [L]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^albums/?(.+/?)?$ $1 [R=301,L]
...
# Catch-all - everything else gets handled in PHP for compatibility.
RewriteRule ^(.*)/?$ index.php?album=$1 [L,QSA]
The third rule should do it, but somehow it doesn't. In the end it falls back on the catch all rule

When you say "rewrite A to B", usually, "A" is what the user types and "B" is where you want him to get.
If A='/admin' and B='/zp-core/admin.php' then the first rule is ok, if you remove other rules, it will work.
The problem is - apache looks into .htaccess on every request, including sub-requests, and your redirect does a sub-request... So it does not match the first rule anymore, but then comes the catch-all rule, and it is a match again (you'll see that $_GET["album"] will be /zp-core/admin.php when hitting index.php on such request.
What you need to do is to add RewriteCond either checking that the uri is not /zp-core/admin.php OR that you're not in sub-request...
Either:
RewriteCond %{REQUEST_URI} !^/zp-core/admin.php$
OR:
RewriteCond %{IS_SUBREQ} !=true

Related

mod_rewrite and redirect causing loop

I have problem when I try to redirect and rewrite together.
I have site example.com/show_table.php?table=12 (max 99 tables). I wanted nice links, so I got this .htacces rw rule:
RewriteRule ^table/([0-9]{1,2})$ show_table.php?table=$1 [L,NC]
Now are links something like example.com/table/12 - it's definitely OK. But I want all old links redirect to new format. So I use Redirect 301, I added to .htaccess this code:
RewriteCond %{REQUEST_URI} show_table.php
RewriteCond %{QUERY_STRING} ^table=([0-9]{1,2})$
RewriteRule ^show_table\.php$ http://example.com/table/%1? [L,R=301,NC]
But when I visit example.com/show_table.php?table=12, I receive just redir-loop. I don't understant - the first is rewrite, the second is redirection, there ain't no two redirections. Do You see any error?
Thanks!
Instead of checking REQUEST_URI in the condition, you need to be checking in THE_REQUEST (which contains the full original HTTP request, like GET /show_table.php HTTP/1.1). When Apache performs the rewrite, it changes REQUEST_URI, so to the rewritten value, and that sends you into a loop.
# Match show_table.php in the input request
RewriteCond %{THE_REQUEST} /show_table\.php
RewriteCond %{QUERY_STRING} ^table=([0-9]{1,2})$
# Do a full redirection to the new URL
RewriteRule ^show_table\.php$ http://example.com/table/%1? [L,R=301,NC]
# Then apply the internal rewrite as you already have working
RewriteRule ^table/([0-9]{1,2})$ show_table.php?table=$1 [L,NC]
You could get more specific in the %{THE_REQUEST} condition, but it should be sufficient and not harmful to use show_table\.php as the expression.
You'll want to read over the notes on THE_REQUEST over at Apache's RewriteCond documentation.
Note: Technically, you can capture the query string in the same RewriteCond and reduce it to just one condition. This is a little shorter:
# THE_REQUEST will include the query string so you can get it here.
RewriteCond %{THE_REQUEST} /show_table\.php\?table=([0-9]{1,2})
RewriteRule ^show_table\.php$ http://example.com/table/%1? [L,R=301,NC]

friend url on wildcard subdomains htacces, not working

i have wildcard subdomains sets already and works fine, now i wish have friends url for the content in thats subdomains, the structure of my site is if the user type subdomain.maindomain.com and the .htaccess redirect to
blogs/index.php?user=subdomain
where blogs/index.php receive the param and show the correct content
now i try to make the url function like this
subdomain.maindoamin.com/24/title-of-content
and then .htaccess must result
blogs/index.php?id_content=24&title=title-of-content
i have the next .htaccess
Options +FollowSymLinks
#this force to server the content always without www.
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.(.*)$
RewriteRule ^(.*)$ http://%1/$1 [R=301]
#this is to pass the subdomain like param and show the right content of the user
RewriteCond %{HTTP_HOST} !^www\.misite\.com [NC]
RewriteCond %{HTTP_HOST} ^([a-z0-9]+)\.misite\.com
RewriteRule ^(.*)$ blogs/index.php?url=%1 [QSA,L]
#the next line i can't make work to make nice url
RewriteRule ^/(.*)/(.*)$ blogs/index.php?idP=$1&name=$2 [L]
not working because when i make in index.php
echo $_SERVER['REQUEST_URI'];
don't show idP=24 show /24/title-of-content and i need $_GET(idP)
i really apreciate some light on this stuff i am not expert on htaccess, thanks in advance to everybody.
There are two problems:
The first argument of RewriteRule matches against everything after the slash of the directory .htaccess is in, and before the query string. If .htaccess is in your www-root, and you get the url http://www.example.com/shiny/unicorns.php?are=shiny, you match against shiny/unicorns.php. It will never start with a slash, so ^/ will never match.
Rules are executed in order. If you go to http://sub.example.com/10/unicorns, the second rule will match first and rewrite the request to /blogs/index.php?url=10/unicorns. If you removed the leading slash the third rule would match, but normally you wouldn't want that. You want to have the third rule only match
You want to move the third rule up so it is the second rule. You want to make it more specific to only match with subdomains. You also know the first part contains only numbers, so use that knowledge to prevent blogs/index.php from matching your now second rule. You also need to prevent blogs/index.php from matching the now third rule to prevent it from matching itself. Last but not least I removed [L] from the now second rule, since the third rule will match anyway.
#the next line i can't make work to make nice url
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^([0-9]+)/([^/]+)$ blogs/index.php?idP=$1&name=$2
#this is to pass the subdomain like param and show the right content of the user
RewriteCond %{HTTP_HOST} !^www\.misite\.com [NC]
RewriteCond %{HTTP_HOST} ^([a-z0-9]+)\.misite\.com
RewriteCond %{REQUEST_URI} !/blogs/index\.php
RewriteRule ^ blogs/index.php?url=%1 [QSA,L]

hide html extension + redirect .html version + special case exception

I have a hard time understanding htaccess mod_rewrite and while I found several related questions + answers, I unfortunately can't get my very specific situation to work correctly (mod_rewrite is even after hours of searching a book of seven seals to me to be honest)
I have foo.html and bar.html within my root directory. Now, I'd like to have foo.html as the default directory index (solved, easy), but from there I do not get it.
What I want to achieve is:
hiding the .html extensions
user should be able to type /bar to get /bar.html without seeing the .html (for every .html)
301 redirecting .html version
user should be able to type /bar.html and see /bar in the url (avoid duplicate, for every .html)
The most tricky part:
As foo.html is default directory index, typing / already shows (transparently) /foo.html, but I need typing /foo.html to resolve to / as well as typing /foo to resolve to /
Try putting these rules in the htaccess file in your document root:
RewriteEngine
# 1. hiding the .html extensions
RewriteCond %{REQUEST_URI} ^/(.*?)/?$
RewriteCond %{DOCUMENT_ROOT}/%1.html -f
RewriteRule ^ /%1.html [L]
# 2. 301 redirecting .html version
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^\ ]+)\.html
RewriteCond %1 !foo$
RewriteRule ^ /%1 [L,R=301]
# 3. typing /foo.html to resolve to / as well as typing /foo to resolve to /
RewriteCond %{REQUEST_URI} ^/(.*?/?)foo(\.html)?$
RewriteRule ^ /%1 [L,R=301]
Also, make sure you have Multiviews turned off:
Options -Multiviews
The first rule has 2 conditions, the first groups the URI everything between the first / and a possible last /. The references it using %1 in the next condition which sees if the /path/to/www/document/root/%1.html is a file that exists. If both are true, it internally rewrites the URI to include a .html at the end.
The second rule has 2 conditions, the first matches against the actual request as opposed to the URI (which can change as the rules are being applied and rewrites happen). It sees if there's a request that ends with .html, and if so, the second condition makes sure that it isn't foo.html request (since the last rule handles that). If both are true, then it redirects the browser to the part of the URI without the html, again using %1 to reference the grouping in the match from the first condition ([^\ ]+).
The last rule checks if the request is for a foo or foo.html. If so, redirect removing that part of the URI.
I am using the one below and it works except that, in some strange case when working with a WARRANTY script for a client it just won't do the thing.
However; the other scripts that calls external scripts via iFrame in a sub-folder seems to work with the exception of the WARRANTY script. ...
Options +FollowSymLinks -MultiViews
RewriteEngine on
RewriteBase /
RewriteCond %{ENV:REDIRECT_END} =1
RewriteRule ^ - [L,NS]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*?)\.(php|html?)$ $1 [R=301,NC,NS]
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule (.*)$ $1.html [L,E=END:1,NS]
RewriteCond %{REQUEST_FILENAME}\.htm -f
RewriteRule (.*)$ $1.htm [L,E=END:1,NS]
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule (.*)$ $1.php [L,E=END:1,NS]
I hope this Helps.

.htaccess falling over on trailing slash

TL;DR
How can I get .htaccess to rewrite http://domain.com/images to http://domain.com/images/ (i.e. add a trailing slash to URLs without one)? The URLs can be deeper than one level, for example http://domain.com/images/page/1.
More info
Say I have a URL like this:
http://jamwaffles2/wallpapers
This will redirect to this in the URL bar, with the rewrite rule working fine:
http://jamwaffles2/wallpapers/?page=wallpapers
However
http://jamwaffles2/wallpapers/ (note trailing slash)
Rewrites fine to
http://jamwaffles2/index.php?page=wallpapers (not visible to user)
With a nice http://jamwaffles2/wallpapers/ in the address bar.
The issue here is that when a trailing slash isn't given to the URL, the URL in the address bar changes to a not-so-pretty one. Can someone offer a solution to this?
Here's my .htaccess:
# turn rewriting on
RewriteEngine on
RedirectMatch permanent ^/$ http://jamwaffles2/home
Options +FollowSymLinks
RewriteRule ^([^/\.]+)/?$ /index.php?page=$1 [L,NC,QSA]
RewriteRule ^([^/\.]+)/([^/\.]+)/?$ /index.php?page=$1&var1=$2 [L,NC,QSA]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ /index.php?page=$1&var1=$2&var2=$3 [L,NC,QSA]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ /index.php?page=$1&var1=$2&var2=$3&var3=$4 [L,NC,QSA]
As a side note, there are more levels to the URL; the .htaccess should make that apparent (e.g. http://jamwaffles2/home/page/2).
EDIT
Curiously, this only happens on /wallpapers. If I type in http://jamwaffles2/home it works as expected, but won't work with http://jamwaffles2/wallpapers.
1) Try this directive: Options +FollowSymLinks -MultiViews -- depending on Apache config it can be the deal breaker.
2) Use this code (one of possible variants) to add trailing slash for NON-EXISTING resources ONLY:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*[^/])$ /$1/ [R=301,L]
It will redirect (301 Permanent Redirect) so URL will change in browser (e.g. example.com/hello => example.com/hello/).
If it still does not work (for whatever the reason may be) -- if you can edit Apache config files -- please enable rewrite debugging (RewriteLogLevel 9) and check the rewrite log to see why some URL failing correct rewrite. Every server can be configured differently, so the same rule may work a bit differently in your case.

Remove trailing slash with mod_rewrite

I've tried every single example I could find, they all produce an internal server error. I have these rules set up (this works, no error):
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}/index.php !-f
RewriteRule ^((/?[^/]+)+)/?$ ?q=$1 [L]
So if it's not an existing file or an existing directory with an index.php we redirect. For instance, http://domain.com/foo/bar becomes http://domain.com/?q=foo/bar
Thing is, I want the trailing slash stripped. So take off the /? at the end of the rule. How do I make it so that http://domain.com/foo/bar/ becomes http://domain.com/foo/bar with a visible redirect first (fixing the client's URL), and only then the real, silent redirection to ?q=?
Everywhere I look I see this:
RewriteRule (.*)/$ $1 [R,L]
But it gives me a 500 error if I insert it before my rule.
If foo/bar exists as a real directory, then the server will be redirecting the client to foo/bar/ (with the trailing slash). It has to do that in order for relative URLs to work correctly on the client. If you put in a rule to rewrite that back to foo/bar with a redirect then there will be a loop. An easy way to test if that's happening is to specify a path that doesn't exist at all (I assume from your index.php detection that the directory tree actually exists). The nonexistent path won't trigger the built-in redirect.
If I setup a similar set of rules to yours (plus the suggested slash-removal rule) I can see the difference between a directory that exists and one that doesn't. The ones that don't work as expected, the ones that do cause Firefox to say This page isn't redirecting properly. IE8 says something similar. Perhaps the Apache setup you're using can detect it and turns it into the 500 error?
It looks like the simpler rewrite rule you mention at the end of your question should work. The problem is, the 500 error isn't really helpful in figuring out why it's not working. One way I've found useful in helping debug mod_rewrite errors is to enable it's logging. Add the following to your httpd.conf:
RewriteLog "/usr/local/var/apache/logs/rewrite.log"
RewriteLogLevel 3
Then try again, and look in the log to see what's going on. Once you're done, you can disable the log be setting the rewriteloglevel 0. See the mod_rewrite docs for details.
Try this rule in front of your current rule:
RewriteRule (.*)/$ /$1 [R,L]
Try these rules:
#prevent mod_dir from adding slash
DirectorySlash Off
#redirect /folder/ to /folder
RewriteCond %{THE_REQUEST} ^GET\s\S+/(\?\S+)?\s [NC]
RewriteRule ^(.*)/$ /$1 [R=301,L,QSA]
#internal redirect for directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} !/$
RewriteRule ^(.*)$ /$1/ [L]

Resources