"没有可接受的变体QUOT;从多视图中的Apache [英] "no acceptable variant" from MultiViews in Apache

查看:248
本文介绍了"没有可接受的变体QUOT;从多视图中的Apache的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个基于PHP的应用程序的一个部署,Apache的多视​​图选项被用来隐藏一个请求分发脚本的PHP扩展。例如。于请求

  /页/约

...将由

办理

  /page.php

...与请求的URI提供尾随部分 PATH_INFO

这其中大部分工作正常,但偶尔也会导致这样的错误

时间

  [错误] [客户86.x.x.x]没有可接受的变体:/路径/到/文件/根/页

我的问题是:什么偶尔会触发这个错误,我怎么能解决这个问题。


解决方案

简答

在以下所有可能会出现此错误是同时成立:


  • 您的Web服务器有在Multiviews 启用

  • 您是允许在Multiviews通过分配它们与将AddType 指令的任意类型,最有可能与这样的产品线以满足PHP文件:

    将AddType应用/ X的httpd - PHP的.php


  • 您的客户端的浏览器请求的 接受 头不包括 * / * 作为一个可以接受的MIME类型(这是极不寻常的,这就是为什么你看到的错误很少)。

  • 您有您的 MultiviewsMatch 指令设置为 NegotiatedOnly 。

您可以通过添加下面的咒语,Apache配置解析错误:

<文件* .PHP>
    MultiviewsMatch任何
< /文件>

说明

了解正​​在发生的事情在这里至少需要获得Apache的的 mod_negotiation模块 和HTTP的接受接受-美孚头。此前创下的OP描述的错误,我也不知道其中任一;我有 mod_negotiation模块启用不是刻意的选择,而是因为这是如何 apt-get的设置Apache对我来说,我不得不启用多视​​图无,除了影响太多了解,这将让我离开的.php 离年底我网址。你的情况可能是相似或相同的。

因此​​,这里是我不知道的一些重要基本面:


  • 请求头像接受接受语言让客户端指定哪些MIME类型或语言,它是可以接受的他们接受了可接受的类型或语言的响应,以及​​指定加权preferences。 (当然,这些都是如果服务器具有或能够产生的唯一有用的,基于这些头不同的反应。)例如,铬发送折以下标题对我来说,每当我加载一个页面:

    <$c$c>Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    接受编码:gzip,紧缩,SDCH
    接受语言:EN-GB,EN-US; Q = 0.8,连接; Q = 0.6


  • Apache的 mod_negotiation模块可以让您存储多个文件,如 myresource.html.en myresource.html.fr myresource.pdf.en myresource.pdf.fr 在同一个文件夹中,然后自动使用请求的接受 - * 标题来决定当客户端发送到 myresource 。这样做有两种方式。首先是建立在同一个文件夹中的类型地图文件明确声明MIME类型和语言每个可用的文件。另一种是在Multiviews。


  • 当在Multiviews启用...


      

    在Multiviews


      
      

    ...如果服务器收到请求/一些/目录/ foo的 /一些/目录/ foo的不存在,则服务器会寻找一个名为 foo的所有文件的目录。* ,并有效假货一个类型的地图,名称所有这些文件,赋予它们的不同的介质类型和内容编码它会如果客户曾要求按名称其中之一。然后,选择客户要求的最佳匹配,并返回该文档。



这里要注意的重要一点是,接受头仍被即使在Multiviews Apache的尊重启用;从类型映射的方法,唯一的区别是,Apache是​​从他们的文件扩展名,而不是通过你在一个类型映射明确声明它推断MIME类型的文件。

没有可接受的变种的错误被Apache抛出(和406响应发送)当存在文件已收到的URL,但它不能提供任何他们,因为他们的MIME类型不符合任何请求的规定的可能性接受头。 (同样的事情可以发生,如果有,例如,在一个可以接受的语言无变体。)这是一个符合HTTP规范,其中指出:


  

如果一个Accept头域present,如果服务器不能发送根据联合接受字段值这是可以接受的响应,那么服务器应该发送一个406(不接受)响应。


您可以测试这种行为很轻松了。只要创建的test.html 含有与在Multiviews Apache服务器启用的根目录的字符串Hello World被称为一个文件,然后尝试用Accept头的许可证要求它HTML响应与一个不。我在这里展示这个我的地方(Ubuntu的)机上的卷曲

$卷曲--header&ldquo;接受:text / html的&QUOT;本地主机/测试
你好,世界
$卷曲--header&ldquo;接受:图像/ PNG&QUOT;本地主机/测试
&LT; D​​OCTYPE HTML PUBLIC&QUOT;! - // IETF // DTD HTML 2.0 // EN&QUOT;&GT;
&LT; HTML和GT;&LT; HEAD&GT;
&LT;标题&GT; 406不可接受&LT; /标题&GT;
&LT; /头&GT;&LT;身体GT;
&LT; H1&GT;不接受&LT; / H1&GT;
&LT; P&gt;该请求的资源/测试的一个适当的再presentation不能在此服务器上找到&LT; / P&GT;
可用变量:
&LT; UL&GT;
&LT;立GT;&LT; A HREF =&QUOT; test.html的&QUOT;&GT;的test.html&LT; / A&GT; ,类型text / html&LT; /李&GT;
&LT; / UL&GT;
&LT;小时&GT;
&LT;地址&gt;的Apache / 2.4.6(Ubuntu的)在本地主机端口80℃的服务器; /地址&gt;
&LT; /身体GT;&LT; / HTML&GT;

这给我们带来了一个问题,我们现在还没有解决:如何 mod_negotiate 确定MIME类型决定是否可以成为它的时候一个PHP文件?由于文件将被执行,并能吐出任何内容类型头它喜欢的类型不是在执行之前已知的。

那么,在默认情况下,答案是多视图根本不会成为的.php 文件。但有机会,你接下来的互联网上很多很多的职位之一的意见(我在第一页上获得4,如果我谷歌的PHP阿帕奇多视图,显然是href=\"http://stackoverflow.com/a/14306831/1709587\">顶个的 将AddType应用/ X的httpd - PHP的.php

咦?为什么这个神奇让Apache竭诚为的.php 文件?当然,浏览器不包括应用程序/ X的httpd - PHP 因为他们会在他们的接受头?

好吧,不完全是。但是,所有的主要的做包括 * / * (从而允许任何MIME类型的响应 - 他们正在使用的接受只为前pressing preference加权头,的的为限制类,他们会接受的。)这将导致 mod_negotiation模块要愿意选择和服务的.php 只要文件作为一些MIME类型 - 任何在所有! - 与它们相关联

例如,如果我只是输入一个URL到铬或Firefox浏览器,在地址栏中的接受头的浏览器发送的,在Chromium的情况下...

<$c$c>Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

...并在Firefox的情况:

接受:text / html的,是application / xhtml + xml的,应用/ XML; Q = 0.9 * / *;问= 0.8

这两种头包含 * / * 作为一个可以接受的内容类型,从而使服务器提供的任何内容的文件类型它喜欢。但一些不太流行的浏览器的的接受 * / * - 或者只是将其包含在页面请求,不加载的内容时,&LT;脚本&GT; &LT; IMG&GT; 标签,你也许还可以通过PHP服务 - 而这也正是我们的问题就来了从。

如果您检查导致406错误的请求的用户代理,你可能会看到,他们从相对不寻常的用户代理是。当我经历了这个错误,那是当我有的src &LT; IMG&GT; 元素指向一个PHP脚本动态提供图像(从URL省略了的.php 扩展名),我第一次亲眼目睹了失败的黑莓用户:

的Mozilla / 5.0(黑莓手机; U;黑莓9320; FR)为AppleWebKit / 534.11 +(KHTML,例如Gecko)版本/ 7.1.0.714移动版Safari / 534.11 +

要解决这个问题,我们需要让 mod_negotiate 通过比给他们一个任意类型,然后依赖于浏览器等发送<$一些手段服务于PHP脚本C $ C>接受:* / * 头。要做到这一点,我们使用 MultiviewsMatch 指令指定多视图可以为PHP文件,无论它们是否匹配请求的接受头。默认选项是 NegotiatedOnly


  

NegotiatedOnly 选项提供了基本名称下面每一个扩展名必须关联到一个公认的 mod_mime 扩展内容协商,如:字符集,内容类型,语言或编码。这是最严格落实以最少意外的副作用,而且是默认的行为。


但是,我们可以得到我们想要的任何选项:


  

您可能会最终允许任何扩展匹配,即使的 mod_mime 无法识别的扩展名。


要限制这种规则的改变只是的.php 的文件中,我们使用的 &LT; FilesMatch&GT; 指令,如:

&LT;文件* .PHP&GT;
    MultiviewsMatch任何
&LT; /文件&GT;

和与微小(但难以数字输出)的变化,我们就大功告成了!

In one deployment of a PHP-based application, Apache's MultiViews option is being used to hide the .php extension of a request dispatcher script. E.g. a request to

/page/about

...would be handled by

/page.php

...with the trailing part of the request URI available in PATH_INFO.

Most of the time this works fine, but occasionally results in errors like

[error] [client 86.x.x.x] no acceptable variant: /path/to/document/root/page

My question is: What triggers this error occasionally, and how can I fix the problem?

解决方案

Short Answer

This error can occur when all the following are simultaneously true:

  • Your webserver has Multiviews enabled
  • You are allowing Multiviews to serve PHP files by assigning them an arbitrary type with the AddType directive, most likely with a line like this:

    AddType application/x-httpd-php .php
    

  • Your client's browser sends with requests an Accept header that does not include */* as an acceptable MIME type (this is highly unusual, which is why you see the error only rarely).
  • You have your MultiviewsMatch directive set to its default of NegotiatedOnly.

You can resolve the error by adding the following incantation to your Apache config:

<Files "*.php">
    MultiviewsMatch Any
</Files>

Explanation

Understanding what is going on here requires getting at least a superficial overview of the workings of Apache's mod_negotiation and HTTP's Accept and Accept-Foo headers. Prior to hitting the bug described by the OP, I knew nothing about either of these; I had mod_negotiation enabled not by deliberate choice but because that's how apt-get set up Apache for me, and I had enabled MultiViews without much understanding of the implications of that besides that it would let me leave .php off the end of my URLs. Your circumstances may be similar or identical.

So here are some important fundamentals that I didn't know:

  • request headers like Accept and Accept-Language let the client specify what MIME types or languages it is acceptable for them to receive the response in, as well as specifying weighted preferences for the acceptable types or languages. (Naturally, these are only useful if the server has, or is capable of generating, different responses based upon these headers.) For example, Chromium sends off the following headers for me whenever I load a page:

    Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Accept-Encoding:gzip,deflate,sdch
    Accept-Language:en-GB,en-US;q=0.8,en;q=0.6
    

  • Apache's mod_negotiation lets you store multiple files like myresource.html.en, myresource.html.fr, myresource.pdf.en and myresource.pdf.fr in the same folder and then automatically use the request's Accept-* headers to decide which to serve when the client sends a request to myresource. There are two ways of doing this. The first is to create a Type Map file in the same folder that explicitly declares the MIME Type and language for each of the available documents. The other is Multiviews.

  • When Multiviews are enabled...

    Multiviews

    ... 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.

The important thing to note here is that the Accept header is still being respected by Apache even with Multiviews enabled; the only difference from the type map approach is that Apache is inferring the MIME types of files from their file extensions rather than through you explicitly declaring it in a type map.

The no acceptable variant error is thrown (and a 406 response sent) by Apache when there exist files for the URL it has received, but it's not allowed to serve any of them because their MIME types don't match any of the possibilities provided in the request's Accept header. (The same thing can happen if there is, for example, no variant in an acceptable language.) This is compliant with the HTTP spec, which states:

If an Accept header field is present, and if the server cannot send a response which is acceptable according to the combined Accept field value, then the server SHOULD send a 406 (not acceptable) response.

You can test this behaviour easily enough. Just create a file called test.html containing the string "Hello World" in the webroot of an Apache server with Multiviews enabled and then try to request it with an Accept header that permits HTML responses versus one that doesn't. I demonstrate this here on my local (Ubuntu) machine with curl:

$ curl --header "Accept: text/html" localhost/test
Hello World
$ curl --header "Accept: image/png" localhost/test
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>406 Not Acceptable</title>
</head><body>
<h1>Not Acceptable</h1>
<p>An appropriate representation of the requested resource /test could not be found on this server.</p>
Available variants:
<ul>
<li><a href="test.html">test.html</a> , type text/html</li>
</ul>
<hr>
<address>Apache/2.4.6 (Ubuntu) Server at localhost Port 80</address>
</body></html>

This brings us to a question that we haven't yet addressed: how does mod_negotiate determine the MIME type of a PHP file when deciding whether it can serve it? Since the file is going to be executed, and could spit out any Content-Type header it likes, the type isn't known prior to execution.

Well, by default, the answer is that MultiViews simply won't serve .php files. But chances are that you followed the advice of one of the many, many posts on the internet (I get 4 on the first page if I Google 'php apache multiviews', the top one clearly being the one the OP of this question followed, since he actually commented upon it) advocating getting around this using an AddType header, probably looking something like this:

AddType application/x-httpd-php .php

Huh? Why does this magically cause Apache to be happy to serve .php files? Surely browsers aren't including application/x-httpd-php as one of the types they'll accept in their Accept headers?

Well, not exactly. But all the major ones do include */* (thus permitting a response of any MIME type - they're using the Accept header only for expressing preference weighting, not for restricting the types they'll accept.) This causes mod_negotiation to be willing to select and serve .php files as long as some MIME type - any at all! - is associated with them.

For example, if I just type a URL into the address bar in Chromium or Firefox, the Accept header the browser sends is, in the case of Chromium...

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

... and in the case of Firefox:

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Both of these headers contain */* as an acceptable content type, and thus permit the server to serve a file of any content type it likes. But some less popular browsers don't accept */* - or perhaps only include it for page requests, not when loading the content of a <script> or <img> tag that you might also be serving through PHP - and that's where our problem comes from.

If you check the user agents of the requests that result in 406 errors, you'll likely see that they're from relatively unusual user agents. When I experienced this error, it was when I had the src of an <img> element pointing to a PHP script that dynamically served images (with the .php extension omitted from the URL), and I first witnessed it failing for BlackBerry users:

Mozilla/5.0 (BlackBerry; U; BlackBerry 9320; fr) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.714 Mobile Safari/534.11+

To get around this, we need to let mod_negotiate serve PHP scripts via some means other than giving them an arbitrary type and then relying upon the browser to send an Accept: */* header. To do this, we use the MultiviewsMatch directive to specify that multiviews can serve PHP files regardless of whether they match the request's Accept header. The default option is NegotiatedOnly:

The NegotiatedOnly option provides that every extension following the base name must correlate to a recognized mod_mime extension for content negotiation, e.g. Charset, Content-Type, Language, or Encoding. This is the strictest implementation with the fewest unexpected side effects, and is the default behavior.

But we can get what we want with the Any option:

You may finally allow Any extensions to match, even if mod_mime doesn't recognize the extension.

To restrict this rule change only to .php files, we use a <FilesMatch> directive, like this:

<Files "*.php">
    MultiviewsMatch Any
</Files>

And with that tiny (but difficult-to-figure-out) change, we're done!

这篇关于&QUOT;没有可接受的变体QUOT;从多视图中的Apache的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆