Yesterday, I lost at least two hours trying to figure out why my local copy of a website was throwing 404 (File Not Found) errors. We had just implemented some URL rewriting on the production site and things appeared to be working fine. On my local computer, however, the URL rewriting would only work for some URLs. After a lot of Googling, a little bit of a meltdown, and walk around the block to clear my head, I finally figured out what was going wrong: MultiViews.
For this particular website, we were using Apache to create a more resource-oriented URL scheme. So, where we used to have URLs that looked like this:
... we were going to be moving to a URL scheme that looked more like this:
For the majority of URLs in the application, this was working fine. But for a few URLs, this came back as a 404 File Not Found error. When I looked in the Apache error logs, I kept seeing log items like this:
[Thu Jun 23 16:55:06 2011] [error] [client 127.0.0.1] Negotiation: discovered file(s) matching request: /Sites/something.com/users (None could be negotiated).
My experience with the Apache HTTP server is somewhat limited; as such, this error didn't really mean anything coherent to me at the time. And, much of my Googling wasn't really yielding anything too valuable. Finally, however, I did come across a threaded discussion that mentioned "MultiViews". On the Apache website, the effect of Multiviews, as listed under Content Negotiation, 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.
This is exactly what was happening! When the user requested the non-existent URL:
... Apache was finding this physical file on a per-directory basis:
... and then using Content Negotiation to try and figure out which file it should served up based on the request headers. And, since I didn't have any file types configured for content negotiation, Apache didn't know how to respond and just returned a 404.
When I checked in my Virtual Host configuration, sure enough, MultiViews was enabled:
- Options Indexes FollowSymLinks MultiViews
The moment I removed the MultiViews option:
- Options Indexes FollowSymLinks
... everything started working fine.
This goes to show you how bad it is to simply enable settings when you are not sure what they do. I've grown to love my Apache server; but, clearly, there is so much more that I need to learn about it. When it works, it works; but, when it doesn't, I have no idea what's going on.
Yeah, these Apache configurations can be rather arcane.
I was not in a good place yesterday :) I swear, I almost just lost it!!
One way to avoid these Apache default directives from affecting you is to always set your DocumentRoot to an empty non-public directory.
Then you can define virtual host files with concise rules, typically in the other or extra directories. The very last lines of tour httpd.conf file point to one of these folders and loads all .conf files located there.
A vhost file typically only requires two sections: VirtualHost, where the site's domain, logs, etc. are setup and a Directory section with all your rules.
I think this (what you are saying) is what my Apache config actually had in it right out of the box. It set some root directory and turned off all options. It was totally *me* who added MultiViews in the Virtual Host option set. I think I just turned on everything that was there since I had no idea what any of it did :)
Yes, I looked at the original* http.conf files on my iMac and Linux server and Mac and those directives are there by default for the DocumentRoot. Maybe you copied those settings elsewhere without reading up on them. :)
*Call me crazy, but I do keep a copy of the original for most server configs.
Yeah, I probably did :) I come from an IIS background (not that I ever knew IIS all that well) and I think I was just thrilled that I could get Apache to work at all!
I know how you feel, I'm still learning this stuff. Sometimes even on live servers!
I am also curious as to your use of follow symlinks too ? :]
I tried to remove the FollowSymLinks and it seemed to break the URL Rewriting. I just did a bit of Googleing and found:
"Options +FollowSymLinks is an Apache directive, prerequisite for mod_rewrite"
So, it must be doing something important.
Interesting, I do not use that at all, unless I need to link directors (symlink) as I have no need. My computer, dev and production environments all run on Ubuntu though. Windows is increasingly becoming enigmatic to me.
SymLinks is something windows doesn't really have, afaik so I was curious as to why it might be needed.
For sake of argument this is what my personal environment looks like for a random site. As I do not care about local security really, it makes things quick.
<Directory /home/brad/workd/hv/hv >
Allow from all
I then have my rewrite in .htacess which also doesn't use follow symlinks.
for simplicity it's simply
RewriteRule ^(.*)$ index.php/$1 [L]
Super curious. Maybe that's what the docs mean by having it
The rewrite engine may be used in .htaccess files and in <Directory> sections, with some additional complexity.
So, since I am using .htaccess I don't need it. You may be using directory so you do ?
instead of ?action=view&id=15
I use ones like ?view=15
seems a bit simpler to may, any thoughts people?
@David, we Mac folks tend to separate target and action. It's built right into Xcode 4, in fact. To establish a target/action connection, you use drag-and-drop in the Interface Builder window. Drag from a control to the setter method of a value you want it to control, for example.
In many ways, it's like the CSS cascade. That's a REALLY arcane analogy, so let me explain:
Suppose you've defined class="major" hotlinks to be in a larger font than those that don't have that class. You can also define that all hotlinks to :hover in some cool way unrelated to font size. The class="major" hotlinks will highlight that way too, because you've separated target and action.
By defining ?action=view&id=28, Ben has allowed his code to do cfif IsDefined("URL.id") and establish identity in a way that can be shared across numerous actions. So the complexity is encoded into the data structure (ampersand-delimited list of equals-delimited lists), allowing the underlying code to be simpler.
Not really sure I see the comparison between this situation and multi-inheritence in css.
In my mind this situation is closer to calling a method on a static object and passing it the ID of the instance to perform the relevant action on:
seems very similar, from a programming syntax point of view, to:
and the are often times an action may not need an ID, like creating a new object.
Just seems that using 2 variables is not necessary in this circumstance.
@David, well, I said it was an arcane analogy.
The idea is that, by not hardwiring targets to actions up front, you allow reuse of parts and reduce combinatorial explosion. In the CSS example, combinatorial explosion would have been defining a.major, a.major:hover, a.minor, a.minor:hover, etc, with plenty of redundancy across the selectors. But defining only a.major and a:hover, you reuse the :hover properties without redundancy. No class="minor" needed. By doing less, more gets done. Probably faster too.
Perhaps a better analogy would be normalization of a relational database. Separating target and action is logical atomism. Making "view" imply both action and id, you are denormalizing the interface. As all theorists acknowledge, sometimes it's just practical to denormalize, but the converse is also true. Sometimes it's just practical to normalize.
Again not really seeing how this relates to normalization, the core aspect of normalization is elimination of redundant and/or duplicate data. Another very small benefit of my style is it can allow calling 2 methods in 1 http call, ie: widget.cfm?Delete=#ThisID#&View=#NextID# , I don't use it that way very often, but it can be handy in some circumstances.
To be honest, some of what you are saying goes over my head. I am not that great at Apache (I'm running on Mac locally, FYI). It might have to do with the way I have the core httpd config set up?
I think what Steve is saying is that you can break apart different aspects of the routing. So, for example, if I use Action and ID, I can apply uniform security (for example) before I even get to the part of the page where the "ID" comes into effect.
For example, I might have something like this in my request handling (pseudo code):
- <cfif not userCanPerformAction( user, url.action )>
- <cfthrow type="AccessDenied" />
By using a uniform action variable, I can use a bit of a simpler divide-and-conquer approach to routing rather than having to figure out special variables for each.
Of course, this all depends on the underlying framework you have to be using. I use a front-controller style workflow where most things go through index.cfm. But, if you use a page-controller style workflow where things typically all different top-level pages, I see no reason again your approach.
Thank you for this post!
I don't use WebDav generally, but was setting it up on my Ubuntu box at home to allow me to synchronize my KeePass database more easily. When I'd try to save any changes to the webdav area I was getting "404 file not found" and it left me with a KeePass.kdbx.tmp file. Removing "MultiViews" fixed it.
I would never, ever have found this on my own.
I'm leaving this (admittedly mostly extraneous) here in case other KeePass users come googling.
My pleasure! I posted this up so hopefully people wouldn't have to bang their heads against the wall. I make no joke when I say that I lost over 2 hours of time just trying to figure this baby beast out!
A little Addendum:
For those people, who have no access to the Apache Configuration (as is the case with my Provider, i cant turn off MultiViews):
Simply avoid filenames that match the virtual directory names.
In my Case:
was rewritten to
and resulted in above mentioned error.
Renaming kunden.php to kundenseite.php resolved the problem, as apache had nothing left to negotiate!
THX to Ben for this post, it pointed my the right direction - Very helpful!
Excellent tip! Thanks. I hadn't even thought of making that recommendation.
Thanks for this post :).
Today, it was very useful for me.
Thank you, you just solved the problem with syncing my mobile with my dav server. :-)
Thanks a million Ben!
I don't want to think about the time I'd have lost if this post didn't pop in my google search !!
And so well explained !
No problem! As someone [me] who is relatively new to Apache, it's super powerful, but definitely throws a number of curve-balls if the configuration is not quite right! I know enough to get things done; but, I'd love to have a better handle on the Web Server layer so that I could tap into even more of the power.
Thanks a lot !
You saved my afternoon :D
Thank you! Everything works now ;)
Very often when I "google" a problem, your posts are first to appear. More often than not, you have a solution. Thank you for posting your struggles to help those of us who have less tolerance to struggling. ;-)
Wow, you just saved me a tremendous amount of time and puzzlement. Many thanks.
Thanks! Saved me a lot of time and frustration!
Thanks so much for this. I just did a fresh install of ubuntu 12.10 and have almost gotten my system to where I want it to be only try to login to one of my dev sites to get this. Its strange though that I should be getting this error as having Multiviews has never caused me any problems. Anyway off to clock some billable hours :)
Thanks man, you safe my ass
Same problem, googled the error.log-message, first hit: your straightforward blog entry. :-)
Just put this line in my Wordpress .htaccess:
- Options -MultiViews
Now I can access both different pages: /page and /page.php
Thank you very much for this article.
Hey Ben, Thanks a lot! You really saved my day!! :)
I had a similar error and was confused why, in development everything worked fine yet on my new testing server, all requests other than the home page failed. Thanks for the link.
I had exactly the same issue. Now to find out what mod was enabled on my dev box, that wasn't on my testing box.
Thank you VERY much! I was tearing my hair out on this one. We have our site set up to redirect 404s to our homepage. It was working on ANY other URL I entered that was bogus, but this ONE that was legit was throwing an immediate 404, not even running my site code. I knew it had to be Apache but had no idea what the errors meant. You saved me hours, thank you!!
I'm glad this is helping people! It's a shame when little config issues like this will wastes HOURS of time!
I just wanted to thank you for this post. I was trying to use Apache to host a Keepass2 database and was unable to save until I removed MultiViews.
I must have looked though a dozen other websites before this one, and tried many other possible solutions! So simple in the end...
Yeah, that's it! Thank you very much for this hint!!
Dude, you just saved me from hours of debugging. Thanks a lot!
First search result on Google. Saved me from having to look any further. Thanks.
First result also for me, thank you, you saved me a lot of time :-)
THANK YOU. Took me 2 days to figure this out.
Thank you, for your information. I was searching for this solution over for 2 weeks. I could find it in Russian web internet, but there is it. Great, IT WORKS!!!
Very helpful! Thanks!
Thank you so much!
Like many other commenters already said: Thanks.
1st hit on google and saved my ass!
This worked for me:
You can remove MultiViews although the purpose is to speed up server processing.
So if you have www.domain.com/index
its naturally going to look for index.* (html,htm etc)
php is not considered a default in this instance until you tell the server to look for it.
So yes you can get round this by omitting the Multiviews.
Alternatively you could make sure they are included in your default mime.typesconfiguration
On Linux is located
/etc/mime.types and uncomment the lines that refer to php
IE - remove the comment hash # from these lines
application/x-httpd-php phtml pht php
If you do not have access to this file or are on shared hosting, you can add them to you .htaccess file in the root of your hosting.
Thank youuuu sooo much :D I almost spent more than 20 minutes debugging this error :D
Thanks Ben, think you have probably just saved me the 2 hours it took yourself to figure this one out!!! Was getting the same on my Mac and was about to start pulling my hair out...
Thank you very much! You saved the day!
Many already said that to you but anyway : thank you ! You saved my day :)
See ya !
THANK YOU!!!! I definitely lost a couple hours and NEVER would have figured this out.
Mate, you just spent two hour,
I was working in a day and spent whole day and end of day, I removed every thing in the .htacess. but your topic really help and Next time I like to search and found some one like you who got such experiences and tell the solution.
You saved my day! Thanks very much!
Very useful thanks !
I spent almost an entire day trying to figure out why my route didn't work, until I find this post.
This post saved me much wailing and gnashing of teeth. Never come across it before somehow, I must have copy/pasted some default config. Thanks for taking the time to write it up.
Thank you sir!! I've been banging my head over this for hours. I went as far as to set 'AllowOverride All' in every Apache configuration file I could dig up on my server in an attempt to solve the 404 errors. After I undid all my changes and simply removed MultiViews from the sites-enabled configuration file, everything works! I completely agree on your point about enabling options that we don't understand, especially since the only reason I had MultiViews in there was that I saw it in a couple examples online. It's hard to focus on Apache2 server management when my primary purpose is to prototype a website on a local server before it goes live, but this problem goes to show that I should spend some time learning about the backend before going back into web development. Again, THANK YOU!!
Been fighting with getting my dev environment set up on my Mac and once again the great Ben Nadel and his blog come to the rescue to fix my only remaining issue and this time it wasn't even CFML related!!! Thanks Ben.