Rewriting URL over a file .htaccess - apache

Here is my problem.
I know how to rewrite a URL only if the file doesn't exist.
But I came across a problem that I have never encountered before.
Given the URL : http://www.my-host.com/agences/my-agencies
With at the directory root 2 files :
agences.php
.htaccess
In the .htaccess :
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^agences/(.*) /agences.php?agence=$1
This does not redirect to the /agences.php and is not even interpreted.
If I change the RewriteRule by:
RewriteRule ^agences/(.*) $1
It doesn't even process the rewrite rules.
And so even if I prepend the slash to the regex condition like this :
RewriteRule ^/agences/(.*) $1
I run on an apache 2.4.10, with the AllowOverride all configure in the vhost.
Thanks for the help.

Add that at the beginning of the code:
Options -MultiViews
The effect of MultiViews is as follows: if the server receives a
request for /some/dir/foo, if /some/dir has MultiViews enabled, and
/some/dir/foo does not exist, then the server reads the directory
looking for files named foo.*, and effectively fakes up a type map
which names all those files, assigning them the same media types and
content-encodings it would have if the client had asked for one of
them by name. It then chooses the best match to the client's
requirements. http://httpd.apache.org/docs/2.0/en/content-negotiation.html

Related

How to avoid the need of typing .php on the url?

I'm on MacOs Big Sur, using Apache and PHP. What I want is: not needing to put .php on the end of my files to load it.
For instance, instead of typing this on the URL:
127.0.0.1/public_html/home.php
I want just to type
127.0.0.1/public_html/home
To achieve this, I'm using this code in .htaccess:
RewriteEngine On
Options -Indexes
DirectoryIndex home.php index.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.+)$ $1.php [L]
The code above works on my hosting, but for some reason, it does not work on my development machine. Instead, a get a 404 error.
The .htaccess file with the code is on the root of public_html folder.
What am I missing?
By typing some "nonsense" at the top of the .htaccess file and not getting an error (ordinarily you would get a 500 Internal Server Error) it would seem that .htaccess overrides were not enabled on the server. So, .htaccess files were effectively disabled - which they are by default on Apache 2.4.
To enable .htaccess overrides (to allow .htaccess to override the server config) you need to set the AllowOverride directive in the appropriate <Directory> container in the server config (or <VirtualHost> container). The default on Apache 2.4 is AllowOverride None.
With the directives as posted you would need a minimum of:
AllowOverride FileInfo Indexes Options
FileInfo for mod_rewrite, Indexes for DirectoryIndex and Options for Options and related directives.
Although it is common (and easier) to just set:
AllowOverride All
Reference:
https://httpd.apache.org/docs/2.4/mod/core.html#allowoverride
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.+)$ $1.php [L]
These directives are not strictly correct. Whilst they may work OK for the URLs you are testing, they would result in a rewrite-loop (500 error response) if you simply append a slash to your URLs (and there is no directory by that name), eg. /home/ (or /home/<anything>). This is because your condition that tests for the presence of the .php file is not necessarily the same as the URL-path you are rewriting to. See my answer to the following question on ServerFault for a thorough explanation of this issue: https://serverfault.com/questions/989333/using-apache-rewrite-rules-in-htaccess-to-remove-html-causing-a-500-error
Also, there's no need to check that the request does not map to a directory to then check if the request + .php extension maps to a file. If the request maps to a file then it can not also be a directory, so if the 2nd condition is true, the 1st condition must also be true and is therefore superfluous.
And there's no need to backslash-escape literal dots in the RewriteCond TestString - this is an "ordinary" string, not a regex.
So, these directives should be written like this instead:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule (.+) $1.php [L]
(RewriteBase should not be used here.)
You can further optimise this by excluding requests that already contain what looks like a file extension (assuming your URLs that need rewriting do not contain a dot near the end of the URL-path). For example:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}.php [L]
(With this 2nd version, it does not matter if RewriteBase is set - it is not used.)
DirectoryIndex home.php index.php
You gave an example URL of /public_html/home (to which .php is appended). However, this DirectoryIndex directive allows home.php to also be served when simply requesting the directory /public_html/. It should be one or the other, not both.

List directory contents with Apache

Let me start by saying that my knowledge of Apache is almost none, so I apologize if I am not using the correct terminology.
I have a website written in Vue, and the routing is taken care by Vue Router. In their documentation, they specify that in order for the router to work correctly, you have to put this in the .htaccess file of your website:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
From what I have been able to understand, all requests are sent back to the index.html file which will take care of loading the correct component based on the path.
My goal is to now allow my website to have a path (let's say /documents) which is not picked up by Vue, but instead shows the contents of the directory and allows you to both navigate and download the contents (Like this).
I have tried a few ways, but they all return a 403 or 500 (possibly due to a mistake in my config). I understand that I need to add a RewriteRule but all of those that I tried return weird errors.
Thanks in advance
You can have multiple rewrite rules based on what the RewriteBase is . In your current set, the rule is applying to the root of the host.
You can add another rule with RewriteBase /documents/. More info: What does RewriteBase do and how to use it?
I recommend reading the docs: https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html
The RewriteCond directive defines a rule condition.
So here a dirty explanation:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
So your RewriteConds says that if the given path/url isn't a file (!-f) and not a directory (!-d) then the next rewrite rule (RewriteRule . /index.html [L]) takes action.
RewriteRule . /index.html [L]
"." is a wildcard, so all urls will be redirect to index.html.
The [L] flags stops the execution (https://httpd.apache.org/docs/2.4/rewrite/flags.html#flag_l)
The RewriteRule ^index\.html$ - [L] stops the execution if the url is index.html.
So, your rewrite rule fulfill your requirements and seems correct.
When you get a 403 you maybe need to add Options +Indexes to your config or htaccess.
In the end, after looking through the docs, I was not able to understand how to set it up. I found this page, and using option #2 I was able to get the directory to at least show up.
I then added the auth to the folder through the .htaccess file and added the .htpasswd file with the username/password combo
TLDR
Create the folder in the location you want. In my case it was in httpdocs/documents
Create a .htaccess file where you put the following contents:
# Omit this section if you do not need the auth
AuthType Basic
AuthName "restricted area"
AuthUserFile /path/to/your/.htpasswd
require valid-user
Order allow,deny
Allow from all
Options +Indexes
Create the .htpasswd file in the location you specified above. To generate the username/password combo I used this
Any corrections are welcome!

Htaccess is matching directories when a file with the same name exists

In my Apache server, I have the following structure:
/www
.htaccess
api.php
index.php
My .htaccess is a very simple file that redirects all the traffic to index.php:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?path=$1 [L,QSA]
And my index.php file just prints whatever the path is:
<?php print_r($_REQUEST['path']); ?>
So when I try to access to web.com/test, I get a web that outputs Array ( [path] => test ).
However, when I try to access web.com/index or web.com/api, it returns a 404 error. I have checked that this only happens when I try to access a route with the name of an existing file. How can I prevent this behaviour?
Turns out that I have MultiViews enabled in apache2.conf file. From the Apache docs:
The effect of MultiViews is as follows: if the server receives a request for /some/dir/foo, if /some/dir has MultiViews enabled, and /some/dir/foo does not exist, then the server reads the directory looking for files named foo.*, and effectively fakes up a type map which names all those files, assigning them the same media types and content-encodings it would have if the client had asked for one of them by name.
So I just had to add Options -MultiViews either to my apache2.conf (server-wise) or to the .htaccess of my project (project-wise) to prevent that behaviour.

.htaccess rewrite url for missing reources

I would like to rewrite files that don't exist to a php handler. I am currently using this .htaccess file, but it doesn't work as I'd like:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /app/index.php?_url=/$1
When I have no files, it works; it redirects correctly for resource that does not exist to my app, and I am able to capture it.
When I have exactly matching file (e.g.: test.txt and I request /test.txt); it loads test.txt correctly.
However, when I have a partial match (e.g.: test.txt exists, but I request /test); it does not redirect at all. In fact, it gives me the standard Apache 404. I want this to actually rewrite to my app, so I can deal with the request in a different manner.
I'm using pretty much default apache 2.2.22 from Debian. Is there some configuration I am missing, or is this the intended behaviour of the rewrite? Is there a way to achieve what I want?
I think you have MultiViews enabled in your Apache by default. Try adding this line on top of your .htaccess:
Options -MultiViews
With MultiViews Apache does its own rewrites and that usually conflicts with mod_rewrite

mod_rewrite problem: RewriteCond %{REQUEST_FILENAME} !-f matches even when REQUEST_FILENAME shouldn't (fully) match

For some reason this rule
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ ./rewrite.php?p=$1&%{QUERY_STRING} [L]
doesn't work for URLs like this http://site.com/index/var/val
All other URLs work but this doesn't. It starts working when I either remove !-f
part or rename index.php file located in the root to something else (e.g. test.php). So somehow site.com/index seems to be equal to site.com/index.php in the eyes of mod_rewrite? The files are located in the root so there shouldn't be any other (upper) .htaccess files involved. This doesn't happen to index only, for example if I create /something.xml, test.com/something/... will suddenly stop working. This happens on some servers only.
Does anyone know why this could be happening?
PS. /index directory is not present on this server
The faulty module is mod_negotiation, not mod_rewrite.
In debian :
a2dismod negotiation
Edit:
To be a little more specific this is the effect of Multiviews, handled by mode_negotiation. So you could keep the module and remove the MultiViews handling with:
Options -MultiViews
From documentation:
A MultiViews search is enabled by the MultiViews Options. If the server receives a request for /some/dir/foo and /some/dir/foo does not exist, then the server reads the directory looking for all files named foo.*, and effectively fakes up a type map which names all those files, assigning them the same media types and content-encodings it would have if the client had asked for one of them by name. It then chooses the best match to the client's requirements, and returns that document.
I've also solved this problem by removing the MultiViews keyword from the
<Directory>
section from my server configuration.
Hope this helps.
I believe the ${REQUEST_FILENAME} aproaches the file as if it was served to a browser directly.
I had a similar problem with this:
/content/detailed-page (Rewritten URL and parsed by php)
The file was returned to me the same way as:
/content/detailed-page.html (real file)