Use mod_rewrite to create custom directory index in Apache - apache

So, Apache's default directory listing sucks and mod_autoindex's customisation options suck, so I figured I could write some rewrite rules in my server root to redirect any requests to a directory:
<IfModule rewrite_module>
RewriteEngine on
RewriteCond %{REQUEST_URI} -d
RewriteRule .* /index.php?dir=$0
</IfModule>
Now, I get the impression this works at least a little, as turning off indexes still leaves localhost accessible (it displays the index.php), however it doesn't seem to reach subdirectories (which either revert to Apache's directory indexes if the indexes option is on, or give a 403 if they're off).
My question is: can I get this rule to apply globally or is my quest for pretty directory indexes doomed to failure?
Edit: I should note: the above rules are contained in a .htaccess in the server root.
Edit: Ideally the solution, if it exists, would still have DirectoryIndex functionality (that is, index.php etc. will be displayed if it exists),

The -d directory test does only work with absolute file system paths. So either provide an absolute file system path (e.g. by using %{DOCUMENT_ROOT}%{REQUEST_URI} or %{REQUEST_FILENAME} directly) or use -D that makes an additional subrequest to resolve the URI path to a file system path. I’d prefer:
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* /index.php?dir=$0

Related

apache mod_rewrite: How to add different rewrite rules for same directory depending on file existence in other directory?

I'm currently having a bit of trouble to configure the rewrite rule(s) in a .htaccess file for an Apache 2.2 server with mod_rewrite. Here's the general idea of what I want to do:
Let's say the server domain is example.com/, and all requests to example.com/abc/ and paths below that directory shall be rewritten. There are two scenarios:
Requests directly to example.com/abc/ or example.com/abc/index.php shall become requests to example.com/index.php with an additional parameter to indicate the original directory of the request. To give a few examples:
example.com/abc/ ==> example.com/abc/index.php?directory=abc
example.com/abc/?foo=1&bar=2 ==> example.com/abc/index.php?directory=abc&foo=1&bar=2
example.com/abc/?a=b&c=d ==> example.com/abc/index.php?directory=abc&a=b&c=d
... and so on
Requests to files within example.com/abc/ shall become requests to files in example.com/, if these files exist there. parameter to indicate the current directory. To give a few examples:
example.com/abc/image.png ==> example.com/image.png (if the later file exists)
example.com/abc/sub/file.css ==> example.com/sub/file.css (if the later file exists)
example.com/abc/foo/bar/baz/name.js ==> example.com/foo/bar/baz/name.js (if the later file exists)
... and so on.
The content of my .htaccess currently looks similar to this:
RewriteEngine on
Options FollowSymLinks
RewriteBase /
# rule for files in abc/: map to files in root directory
RewriteCond $1 -f
RewriteRule ^abc/(.*?)$ $1
# rule for abc/index.php: map to index.php?directory=abc&...
RewriteCond $1 !-f
RewriteRule ^abc/(.*?)$ index.php?directory=abc&$1 [QSA]
The later rule seems to work, requests to example.com/abc/index.php get rewritten as intended. However, that does not work for files in the abc/ directory.
Any hints on what I am doing wrong here and how to solve that problem are appreciated. What changes have to be applied to .htaccess to get things working as described?
I've found a working solution:
RewriteEngine on
Options FollowSymLinks
RewriteBase /
# rule for file in abc/: map them to files in document root
RewriteCond %{DOCUMENT_ROOT}/$1 -f
RewriteRule ^abc/(.*?)$ %{DOCUMENT_ROOT}/$1 [L]
# rule for abc/index.php: map to index.php?directory=abc&...
RewriteRule ^abc/(.*?)$ %{DOCUMENT_ROOT}/index.php?directory=abc&$1 [QSA,L]
The two main differences are:
using the [L] flag to indicate that no further rules should be considered after a rule matches - that was probably the problem why only the last rule seemed to work.
prefacing locations in the condition and the rewrite location with %{DOCUMENT_ROOT} to get absolute paths.

Is it possible to force apache server to read root htaccess before going into subdirectory?

I have a subdirectory in root (let's call it /sub) and .htaccess in root. If I call the following URL: mywebsite.com/sub, I get directly into subdirectory omitting .htaccess in root, so I wanted to ask whether it is possible to force server to read root .htaccess first before going to subdirectory (and make corresponding redirection if needed)?
Thanks in advance
EDIT: I have discovered that my redirection explicitly excluded directories which was unwanted (and seemed to be the root of my problem):
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) http://redirectwebsite.com/$1 [R=301,L]
I removed the second line, but unfortunately I still get into my folder.
So to make it clear:
when I go to http://mywebsite.com/not-a-folder-in-root, I am redirected to the new site correctly
when I fo to http://mywebsite.com/a-folder-in-root, I don't get redirected and go instead to my subfolder's index.php
The latter is the behavior I want to eliminate.
It depends on what you need to do. First .htaccess files are read in the order in which they are found.
How directives are read
The configuration directives found in a .htaccess file are applied to
the directory in which the .htaccess file is found, and to all
subdirectories thereof. However, it is important to also remember that
there may have been .htaccess files in directories higher up.
Directives are applied in the order that they are found. Therefore, a
.htaccess file in a particular directory may override directives found
in .htaccess files found higher up in the directory tree. And those,
in turn, may have overridden directives found yet higher up, or in the
main server configuration file itself.
So the htaccess file will always override the file in the root directory. However you can manipulate the Apache config file and specify directives in say a Location directive. You can specify certain rules and then it will take affect over .htaccess rule. See info below.
As discussed in the documentation on Configuration Sections, .htaccess
files can override the sections for the corresponding
directory, but will be overridden by other types of configuration
sections from the main configuration files. This fact can be used to
enforce certain configurations, even in the presence of a liberal
AllowOverride setting. For example, to prevent script execution while
allowing anything else to be set in .htaccess you can use:
<Directory "/www/htdocs">
AllowOverride All
</Directory>
<Location "/">
Options +IncludesNoExec -ExecCGI
</Location>
Otherwise if you can't do that change because you don't have access, you would have to do all the rules in the root .htaccess file that also pertains to the sub folder which you can do instead of putting an .htaccess in the sub folder.
Edit based on comment
#just use this for all requests
RewriteRule (.*) http://redirectwebsite.com/$1 [R=301,L]
#Or you can do all non-existent folders and that sub folder too
RewriteCond %{REQUEST_URI} ^/sub [NC]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) http://redirectwebsite.com/$1 [R=301,L]

.htaccess - Redirect all to index.php for root folder or subfolder

I need an .htaccess file that will work whether it is put in the root folder or a subfolder without modification. The script below is the normal one that I've been trying to adapt without success. I tried the solution on htaccess rewrite index.php on root and subfolders and couldn't get it to work.
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>
Layout
.htaccess
index.php
subfolder1
- .htaccess
- index.php
The route /blah should go to /index.php and /subfolder1/whatever should go to /subfolder1/index.php. Currently, the above script will send /subfolder1/whatever to /index.php.
[Update]
This should also work for any path under subfolder1, like /subfolder1/1/2/3/idunno.
If you are using Apache 2.2.16 and later, you can just stop using mod_rewrite, which although extremely useful and powerful, can get messy as hell.
A new directive in mod_dir was introduced, FallbackResource which does just that, redirecting to the uri of your choice if there is no hit on the file system. It is available in .htaccess files as long as AllowOverride Indexes is specified for the directories in the configuration.
As .htaccess files are evaluated depth-first, you just have to have each .htaccess file describe your fallback resource in the current directory, and the one in the subdirectory subfolder1 will take precedence:
subfolder1/.htaccess:
FallbackResource index.php
.htaccess:
FallbackResource index.php
They're both the same, and work just right.
It seems this directive is not well known yet even though it's been around for a few years, and its goal is precisely to solve that problem in an elegant way.
There is only one limitation with that setup. Calling urls in non-existing sub-directories of the root dir or subfolder1 will yield subrequest recursion and subsequently an error 500, because the fallback resource is local to the given directory.
The best approach is to have absolute uris (beginning with '/') as parameter to FallbackResource, which is why it is true that the requirement in itself is kind of odd, and is probably not playing too well with the inner workings of Apache.

Wildcard in URL (mod_alias or mod_rewrite)

I need to have URLs such as mydomain.com/whatever, where "whatever" can be any arbitrary string, all call the same php file where it sorts out what to display (or displays a 404). However, I want files and other php files to work normally (anything that is otherwise aliased, or that actually exists in the file system).
A simple AliasMatch /* myphpfile.php (after all the other Aliases in httpd.conf) works fine on my own setup, but on a production server, the wildcard alias sends all the other php files to myphpfile.php. I'm not sure what else might be confusing things.
Technically the whatever string will be alphabetic and lower case, so it can filter for that, but all attempts I've made with regex's haven't been successful.
Use these rules (you need mod_rewrite):
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteBase /
# do not do anything for already existing files
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .+ - [L]
RewriteRule ([a-z]+) /myfile.php [L]
Place in .htaccess in website root folder. If placed elsewhere some tweaking may be required.
This will rewrite (internal redirect) all NON-EXISTING single-lowercase-word requests to /myfile.php, where using $_SERVER['REQUEST_URI'] script can determine which URL was called and decide what to do (routing).
This will work for URLs like /whatever, but will do nothing for /what-ever, /hello/pinky, /hello123.

mod-rewrite question: /test/method is rewritten to test.svg/method

I noticed an odd (to me) mod_rewrite thing happening. Fixing it is not important to me so much as figuring out what's going on. Basically, I have an svg file called test.svg in my document root, as well as an index.php. My expectation, based on my .htaccess file is that visiting http://localhost/test.svg would get me the .svg file (and it does), while visiting http://localhost/test/action would be rewritten to index.php/test/action. Instead, the latter is apparently rewritten to test.svg/action, as I receive the message
The requested URL /test.svg/action was not found on this server.
Here is my .htaccess file:
# Turn on URL rewriting
RewriteEngine On
# Protect application and system files from being viewed
# RewriteRule ^(application|modules|system) - [F,L]
# Allow any files or directories that exist to be displayed directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Rewrite all other URLs to index.php/URL
RewriteRule .* index.php/$0 [PT,L]
I am using a Apache 2.2.12 on Ubuntu (installed via apt-get). I think my setup is fairly standard, but I'm not sure exactly what directives or config files would be relevant. I am by no means a sysadmin of any kind, I just use this server to test and develop things locally.
As I said, fixing this issue would be trivial, I just am often confounded by mod_rewrite and would like to understand what's going on here.
Apache's HTTP content negotiation feature is automatically translating from "/test" to "/test.svg". See http://httpd.apache.org/docs/2.0/content-negotiation.html#multiviews
You can disable content-negotiation in .htaccess with the directive:
Options -MultiViews
You can get more information about what mod_rewrite is doing by adding these directives to your Apache configuration (they won't work in .htaccess):
RewriteLog /path/to/rewrite.log
RewriteLogLevel 3
The RewriteLogLevel can be any number from 0 (disabled) to 9 (extremely verbose). 3 should give you enough to see what's going on, but don't use that on a production server.