.htaccess matches files even without extension in URL - apache

.htaccess
RewriteEngine On
# If directory, redirect to root
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ / [R,L]
# Don't show something.json
#RewriteCond %{REQUEST_FILENAME} -f
RewriteRule something\.json$ / [R,L]
# If not file or directory, go to the router
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /subfolder/router.php [L]
# Any other existing files get served normally
We can ignore the first two rules, they work. The problem is in the 3rd part/rule.
htaccess file is here:
https://subdomain.sitename.com/subfolder/.htaccess
I need any non-directory and non-file addresses to go to /subfolder/router.php.
I have a html file on the server:
https://subdomain.sitename.com/subfolder/sub2/filename.html
I want the URL:
https://subdomain.sitename.com/subfolder/sub2/filename
to map to /subfolder/router.php, since filename doesn't exist on the server (only filename.html exists).
But it serves/shows the filename.html file/page instead of mapping to /subfolder/router.php. Address bar still shows just filename without .html.
It works fine on my localhost, but not on the online server I uploaded the files to. I don't have access to the config files of the server.
I've read online, and looks like I'm on a virtual host, maybe?
I've tried:
Options -MultiViews (gives error)
AcceptPathInfo off (doesn't help)
RewriteCond %{REQUEST_FILENAME}.html -f (nope)
... some other desperate ways of matching
Is there any way to force RewriteCond %{REQUEST_FILENAME} !-f to not match extensions of files on the server when they are not in the URL?
Or at least somehow fix the problem?
I'm losing my mind right now, after hours of trying things and reading online. Any help would be greatly appreciated.

Okay, after some thinking and trying things out, I solved it by changing:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /subfolder/router.php [L]
to
RewriteRule \.html$ /subfolder/router.php [L]
I'm not entirely happy, but it works for now, as the router.php file checks for file existence and uses a whitelist of files.

Related

Redirecting all urls, including no path, to a file in subdirectory

I have checked a large amount of existing answers regarding .htaccess redirects. However none of them have helped me.
What I want to accomplish is redirecting all request urls to /api/init.php. However I've only gotten so far to where my index page www.example.com simply gives me a file listing because of the missing index.php file, while every url request with a path is working.
How can I accomplish this with .htaccess without ending up with a directory listing on my landing page?
This is as far as I got:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /api/init.php?path=$1 [NC,L,QSA]
Well your site root is a directory, so this rule you have excludes existing directories. What you could do is only exclude existing files, and allow existing directories to be handled by the PHP script. Like this:
RewriteEngine on
RewriteCond %{REQUEST_URI} !=/api/init.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /api/init.php?path=$1 [L,QSA]
I removed the NC flag as it's not needed. I added a condition to prevent an unnecessary file-system check.
You don't have to pass the path on in a URL parameter, as you could get it from $_SERVER['REQUEST_URI'] in PHP (not the same as REQUEST_URI in mod_rewrite, in PHP it always has the original URI). If you wanted to do that then your rule becomes nice and simple:
RewriteEngine on
RewriteCond %{REQUEST_URI} !=/api/init.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /api/init.php [L]
Because the query string will just be passed on unaffected (so QSA is not needed).

Apache 404 url not working

I have a few problems with our company website. My colleague left the .htaccess file inside the _control directory like this:
# Virtual site directory
#
# This script lets us make use of default
# versions of files used in a typical site.
# When a request on the site is made for a file
# that doesn't exist, instead of a 404 we rewrite
# the path to /_control/site/
#
# Any file in the directory this script is in will
# take precedence over this script, so this script
# only comes into play if the file does not exist.
# First, set up URL rewriting
Options +FollowSymLinks
RewriteEngine On
# Add trailing slash for directories
# TODO: fix if site not in root dir (e.g. DEV server)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !.+\.(php|js|css|gif|jpg|jpeg|png)$ [NC]
RewriteCond %{REQUEST_URI} !.*/$
RewriteRule ^(.*)$ /$1/ [L,R=301]
# This Apache mod_rewrite rule checks to see
# if the requested file exists, if it doesn't
# then we rewrite to the respective site location.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !^.*/_control/(admin|common).*$
RewriteCond %{REQUEST_FILENAME} !^.*/_control/site/.*$
RewriteRule ^(.*)$ site/$1 [L]
The structure of my application looks like:
Everything was working OK but about a week ago, our mysite.com/admin and 404 page started redirecting to the hosting company's website. DaveG has already answered first part of the problem here: https://stackoverflow.com/a/26013322/1933380 and I was able to get the virtual directories working. I am still having problems with the 404 page. Am I missing something or making a mistake?
What you are doing now, is telling the webserver to look for all non-existing pages and directories in another folder (_control). This does not give a solution for typo's, non-existing pages etc. If you want to solve this, you could use:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !^.*/_control/(admin|common).*$
RewriteCond %{REQUEST_FILENAME} !^.*/_control/site/.*$
RewriteRule ^(.*)$ site/errhandler.php?file=$1 [L]
and make a file called errhandler.php, which displays a custom 404-page and (for example) give possible alternatives based on a database search or whatever. You could then use $_GET['file'] to display the original filename.

Serve resource files bypassing a framework using .htaccess directives

I'm using the Kohana framework. It has the following configuration of .htaccess file:
# Protect application and system files from being viewed
RewriteRule ^(?:application|modules|system)\b.* index.php/$0 [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 [PT]
I'm particulary interested in the second part Allow any files or directories that exist to be displayed directly. Can someone please elaborate in details on how it's configured?
It is due to 2 condition lines using RewriteCond directive that acts like if condition
RewriteCond %{REQUEST_FILENAME} !-f
Means if request is NOT for a valid file
RewriteCond %{REQUEST_FILENAME} !-d
Means if request is NOT for a valid directory
Both combined with RewriteRule below mean that if request is not a valid file or directory then forward every request to index.php

Apache rewrite condition to fall back when css doesn't exist

I need a rule in Apache that redirects not-found CSS files to another location based on their names in another folder. Like this:
Request: localhost/css/nonexistent.css
Response: localhost/css/g/nonexistent.css
If the CSS exists, just serve it like normal:
Request: localhost/css/existent.css
Response: localhost/css/existent.css
My project is on CakePHP which comes with the following rules by default:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,L]
I mention it because whatever the new rule is, it should not break Cake's rules.
Thanks for the help.
Edit: Forgot to mention that the css/g/ file is an alias for a script (inside the Cake MVC stack) that generates the new css file and echoes it. Answers so far seem to do the redirection fine, but then it doesn't find the css/g/file.css either because it really doesn't exist.
Here is a rewrite rule adapted from this SO question and the Apache mod_rewrite docs.
The gist is: If the request is for a path that starts with /css/,get the filesystem path of the requested file and check if it exists. If id doesn't, rewrite the URL for your deeper directory. This should be placed before the rules you posted in your question.
RewriteCond %{REQUEST_URI} ^/css/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/css/(.*) /css/g/$1
You can try first checking if the /css/g/ css file exists:
# Make sure it doesn't exist
RewriteCond %{REQUEST_FILENAME} !-f
# Make sure this is a request for a css file:
RewriteCond %{REQUEST_URI} ^/css/(.*)\.css$
# See if the /g/ version exists
RewriteCond %{DOCUMENT_ROOT}/css/g/%1.css -f
# rewrite if all conditions satisfied
RewriteRule ^css/(.*)$ /css/g/$1 [L]
The %1 in the 3rd condition backreferences the filename (sans .css extension) matched in the previous RewriteCond.
EDIT:
If the file css file is actually generated, then skip the checking of /g/ version and just pass it to the controller:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} ^/css/(.*)\.css$
RewriteRule ^css/(.*)$ index.php/css/g/$1 [L]

How to hide the .html extension with Apache mod_rewrite

I have a small number of static sites where I simply want to hide the .html extension:
the URL /foo fetches the static file /foo.html
the browser still displays the URL /foo
The client can then send out bookmarks in the style example.com/foo rather than example.com/foo.html.
It sounds very simple, and I've used mod_rewrite happily before (say with WordPress or for redirects), but this is proving much harder to crack that I thought. Perhaps I'm missing something really obvious, but I can't find a solution anywhere and I've been at it all day!
We run our own server, so this can go wherever is the best place.
Addendum
The solution checked below worked fine. Then after running the site awhile I noticed two problems:
all pages began to appear unstyled. I reloaded, cleared the cache, etc., but still no-style. I've had this trouble before, and can't locate the source.
There's a directory AND an HTML file named 'gallery', so the /gallery link shows a directory listing instead of the HTML file. I should be able to sort that one, but further tips welcome :-)
Try this rule:
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule !.*\.html$ %{REQUEST_FILENAME}.html [L]
This will rewrite all requests that can be mapped to an existing file when appending a .html.
The previous answers don't check if the requested path is a directory.
Here is the full rewrite condition which doesn't rewrite, if requested path is a directory (as stated by the original question):
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d # is not directory
RewriteCond %{REQUEST_FILENAME}\.html -f # is an existing html file
RewriteRule ^(.*)$ $1.html # rewrite index to index.html
To be SEO friendly and avoid double content, redirect the .html URLs:
# Redirects example.com/file.html to example.com/file
RewriteCond %{REQUEST_FILENAME} !-d # is not directory
RewriteCond %{REQUEST_FILENAME}\.html -f # is an existing html file
RewriteCond %{REQUEST_URI} ^(.+)\.html$ # request URI ends with .html
RewriteRule (.*)\.html$ /$1 [R=301,L] # redirect from index.html to index
If you need the same for scripts take a look here:
How can I use .htaccess to hide .php URL extensions?
The accepted solution do not works when the website is configured with a virtual host / document root.
There is the solution I used:
RewriteEngine on
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}.html -f
RewriteRule !.*\.html$ %{REQUEST_FILENAME}.html [L]
Look at this post http://alexcican.com/post/how-to-remove-php-html-htm-extensions-with-htaccess/ I haven't tried it yet but the demonstration seems pretty convincing.
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
Wow, I have seldom seen such an issue for which there exists so many "solutions" on the web, where people just throw up what "works for them" but for which few take time to read the documentation to figure out what it does. Many of the solutions given here don't work for virtual hosts, for example.
After much cross-referencing and reading, I want to contribute my own solution that "works for me". Hopefully it works for you, too. I didn't create it from scratch; I was inspired by all the other contributions (even though most of them did not "work for me" without modification).
RewriteEngine on
#if foo requested, return foo.html contents
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}\.html -f
RewriteRule ^(.*)$ $1.html [L]
#redirect foo.html to foo
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteRule ^(.+)\.html$ $1 [R,L]
The [R] flag by default does a temporary (302) redirect; if you want a permanent redirect, use R=301 in place of R.
To remove .html extension from .*.html requests, you can use the following script in root/.htaccess :
RewriteEngine On
RewriteBase /
#1) externally redirect "/file.html" to "/file"
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.html [NC]
RewriteRule ^ %1 [R=301,L]
#2) rewrite "/file" back to "/file.html"
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.*?)/?$ $1.html [NC,L]
the url /foo fetches the static file /foo.html
the browser still displays the url /foo
Apache can do this without mod_rewrite, see documentation:
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.
Source: http://httpd.apache.org/docs/current/content-negotiation.html
Here is an example which allows us to store the file on disk as:
foo.html.php
But in the browser, refer to it as
foo.html
To make this work for you, I think you would just need to modify it a bit to match your existing requests, and check for an actual file in place with the .html extension.
# These are so we do not need the .php extension
RewriteCond %{REQUEST_FILENAME} (\.xul|\.html|\.xhtml|\.xml)$',
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}.php -f',
RewriteRule ^(.*)$ $1.php',