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

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.

Related

accessing same file with different name using htaccess

I am clarify my problem into steps and this help to understand what I am trying to do.
I am trying to build url shotner using htaccess, let say https://example.com/index.php/A43DS4 where index.php is the main file which can be excess as https://example.com/index/A43DS4 by writing this htaccess code:
RewriteEngine On
# by writing the below rule: we can excess the index.php page with:http://example.com/url
# its convert index.php to url/image/invite
# its remove .php extention
RewriteRule ^url?$ index.php
RewriteRule ^image?$ index.php
RewriteRule ^invite?$ index.php
# by writing the below rule: we can excess the URL-CODE with:http://example.com/index/[URL-CODE]
# its convert index.php to index/
# its remove .php extention
# Accepts all alfa-numeric $_GET[link]
RewriteRule ^index/([0-9a-zA-Z]+) index.php?link=$1
but what I want is instead of writing single individual line like:
RewriteRule ^url?$ index.php
RewriteRule ^image?$ index.php
RewriteRule ^invite?$ index.php
I want:
RewriteRule ^url|image|invite?$ index.php
same as for:
RewriteRule ^index/([0-9a-zA-Z]+) index.php?link=$1
to:
RewriteRule ^index|url|image|invite/([0-9a-zA-Z]+) index.php?link=$1
but unfortunatly ^url|image|invite?$ index.php is not working. I am not good at apache. if anybody provide some sort of direction and poinout my mistake it would be a great.
I think this is what you are looking for:
RewriteEngine on
RewriteRule ^/?index/(\w+)/?$ /index.php?link=$1 [END]
RewriteRule ^/?(?:url|image|invite)/?$ /index.php [END]
This rule will work likewise in the http servers host configuration or inside a dynamic configuration file (".htaccess" file). Obviously the rewriting module needs to be loaded inside the http server and enabled in the http host. In case you use a dynamic configuration file you need to take care that it's interpretation is enabled at all in the host configuration and that it is located in the host's DOCUMENT_ROOT folder.
In case you receive an internal server error (http status 500) using the rule above then chances are that you operate a very old version of the apache http server. You will see a definite hint to an unsupported [END] flag in your http servers error log file in that case. You can either try to upgrade or use the older [L] flag, it probably will work the same in this situation, though that depends a bit on your setup.
And a general remark: you should always prefer to place such rules in the http servers host configuration instead of using dynamic configuration files (".htaccess"). Those dynamic configuration files add complexity, are often a cause of unexpected behavior, hard to debug and they really slow down the http server. They are only provided as a last option for situations where you do not have access to the real http servers host configuration (read: really cheap service providers) or for applications insisting on writing their own rules (which is an obvious security nightmare).

htaccess doesn't work after moving from webhost to local Synology host

I have a hosted website where I use the following htaccess file for formatting of urls, These all work fine. The host uses Apache, but unfortunately doesn't show a version number. I think it's 2.4.
Options +FollowSymLinks
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
RewriteRule ^item/([0-9]+)/(.*) /item.php?item=$1&title=$2 [L]
RewriteRule ^category/(.*) /showitems.php?category=$1 [L]
RewriteRule ^search/(.*) /searching.php?options=$1 [L]
RewriteRule ^searching/(.*) /showitems.php?search=$1 [L]
RewriteRule ^update/(.*) /showitems.php?update=$1 [L]
AddType application/x-httpd-lsphp .html .htm .shtml
I copied the entire site to my local Synology Diskstation with Apache 2.4.
The rewrite urls for category, search and update work fine. However, the urls for 'searching' and 'item' return 404 errors. 'Searching' is a header redirect from within 'searching.php'
Item is an oddity in the sense that it uses 2 get params in the result url. In trial and error mode I changed it to:
RewriteRule ^item/(.*) /item.php?item=$1 [L]
Which doesn't work either, however
RewriteRule ^itemitem/(.*) /item.php?item=$1 [L]
Works fine, which really puzzles me. This last rewrite also doesn't work when I add the second parameter again.
What am I missing? Or is there a better way to approach these rewrites in the first place that I could try?
What CBroe has commented on your post is properly the correct answer.
To be more specific and explain why then if your Apache 2.x server has MultiViews enabled it will try to match things up for you like directory names, file names on your behalf to make the user-experience easier.
However, this can in many cases confuse your rewrite rules that are expecting very specific regular expressions.
In most cases you can get away with just disabling MultiViews.
You disable it by adding to your Options in your .htaccess file.
-MultiViews
Please note that if the AllowOverrive directory does not allow this then your need to change the Apache configuration file for that vhost/directory to include the -MultiViews option.
You can read more about MultiViews here:
http://httpd.apache.org/docs/current/content-negotiation.html
The other thing and perhaps more correct way is to build your rewrite rules better and take advantage of things like the "!-f" or "!-d" parameter to let your rules know that you don't want to include files or directories and so forth.
A side note by the way on MultiViews, even though it's very fancy and neato - If you are running a production site please know that MultiViews create a hole lot of unwanted disk I/O and can slow down the Apache servers performance quite a bit! So it's always good practice to disable MultiViews.

.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

Use mod_rewrite to create custom directory index in 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

Apache DirectorySlash Off - Site breaks

If i set DirectorySlash Off in my .htaccess file and call the directory without the trailing slash i get an 403-Forbidden from my server. If i call it with slash everything works fine.
Could anyone explain why? Here are my fully anonymized .htaccess:
# GLOBAL CONFIG
Options +FollowSymlinks
DirectorySlash Off
AddDefaultCharset utf-8
php_value post_max_size 256M
php_value upload_max_filesize 256M
# BEGIN WordPress
RewriteEngine On
RewriteBase /folder/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /folder/index.php [L]
# END WordPress
# REMOVE WWW
RewriteCond %{HTTP_HOST} ^([^.]+)\.domain\.com$ [NC]
RewriteRule ^(.*)$ http://domain.com$1 [R=301,L]
As you know per the documentation, when DirectorySlash is set to Off, requests to /folder do not have DirectoryIndex evaluated. This means that the request will not be automatically mapped to /folder/index.php.
mod_dir performs this check in the "fixup" phase of the request processing. mod_rewrite, which is responsible for your RewriteRule definitions, also performs its processing in this phase when you specify the rules in a .htaccess file.
However, it was programmed with an awareness of modules like mod_dir, and includes a check to make sure that the current directory was requested with a trailing slash. If not, it declines to handle the request, since doing so might lead to undefined behaviour.
The request then moves on to the content-generation phase, which, since the request was not mapped to a real file, is handled by mod_autoindex. Given that Indexes are disabled on your host by default, mod_autoindex returns 403 Forbidden which is what you see.
Note that since DirectoryIndex is not evaluated, even if mod_rewrite were to process the request, it would still fail, because no auto-resolution to index.php would occur, and your rule
RewriteRule . /folder/index.php [L]
wouldn't match, because the . requires a match on something (but the request would be blank).
Enabling DirectorySlash prevents this scenario by correcting the prevented actions in all of the previously mentioned scenarios except the last note, which is taken care of by the fact that DirectoryIndex maps the request to index.php anyway.
With Apache 2.4 you can allow rewrites in .htaccess files by setting RewriteOptions AllowNoSlash.
Changes with Apache 2.3.16
...
*) mod_rewrite: Add the AllowNoSlash RewriteOption, which makes it possible
for RewriteRules to be placed in .htaccess files that match the directory
with no trailing slash. PR 48304.
[Matthew Byng-Maddick <matthew byng-maddick bbc.co.uk>]
...
See Apache documentation of mod_rewrite
I think because when you turn DirectorySlash off, it disable the autocorrection of the url and it is trying to show the directory list but fortunately you have probably disabled this somewhere (or in file permissions) so it sends a 403-Forbidden. I guess that when you turn it on, it works normally.
From what I understand from the docs, it is not very good to use DirectorySlash off for security.
http://httpd.apache.org/docs/2.1/mod/mod_dir.html
As Tom already answered, there is special option for RewriteOptions, but only for Apache 2.3.16+, so if you, like me, have an apache of the older version, then you cannot rewrite url for same directory, because apache doesn't know about this directory.
Example:
"GET /somedir" will point to <Directory /var/www/html/public> in rewrite log, but(!) requested filename (%f) in access log will still /var/www/html/public/somedir/ - this is crazy apache logic. And apache will show you either 503 (without Options +Indexes) or directory listing (otherwise) with wrong urls such as /subdir/ instead of /somedir/subdir/
So, I've found only one worked solution for me - using aliases:
AliasMatch "/somedir$" "/var/www/html/public/somedir/index.html"
Hope this helps someone else in 2020+ :D