简体   繁体   English

如果文件夹存在,则 mod_rewrite 尝试重定向

[英]mod_rewrite tries to redirect if folder exists

I have an HTTP-API, written in PHP and I redirect all calls to my index.php where a (Slim-)router does the rest.我有一个用 PHP 编写的 HTTP-API,我将所有调用重定向到我的 index.php,一个(Slim-)路由器完成其余的工作。 That works.那个有效。

My redirection in .htaccess looks like:我在.htaccess重定向如下所示:

Options +FollowSymLinks
Options -Indexes

RewriteEngine on
RewriteRule ^ index.php [QSA,L]

There is only one problem: If one endpoint is named identically with an existing folder, strange things happen.只有一个问题:如果一个端点与现有文件夹的名称相同,就会发生奇怪的事情。 That's the case when I create an endpoint [PUT] /test , while there is also a folder called test , containing unit tests.当我创建一个端点[PUT] /test时就是这种情况,同时还有一个名为test的文件夹,其中包含单元测试。

This is what is happening:这是正在发生的事情:

  • If I call [PUT] /test with preflight I get no response at all for the OPTIONS call which leads my app to fail.如果我在预检时调用[PUT] /test ,对于导致我的应用程序失败的 OPTIONS 调用,我根本没有任何响应。 That's the real problem case, since I need CORS.这是真正的问题案例,因为我需要 CORS。
  • If I call it without preflight and PUT I get 301 forwarding on the same endpoint.如果我在没有预检和 PUT 的情况下调用它,我会在同一端点上得到 301 转发。
  • If I call it without preflight and POST I get 301 forwarding on the same endpoint, but with GET?!如果我在没有预检和 POST 的情况下调用它,我会在同一个端点上得到 301 转发,但是使用 GET?!

I guess I have to change something in .htaccess to make it ignore the existence of the folder, but what?我想我必须更改.htaccess某些内容以使其忽略文件夹的存在,但是什么?

How to reproduce如何繁殖

Have an Apache server and create directory as following:有一个 Apache 服务器并创建目录如下:

  • /target /目标
  • /target/.htaccess /目标/.htaccess
Options +FollowSymLinks
Options -Indexes

RewriteEngine on
RewriteRule ^ index.php [QSA,L]
  • /target/index.php /目标/index.php
<?php echo $_SERVER['REQUEST_URI'];
  • /target/subfolder /目标/子文件夹

  • /testscript.html /testscript.html

<!DOCTYPE HTML>
<html lang="en">
<head>
    <title>test</title>
    <style>
        div {margin: 0.5em;}
        .error { background: rgba(255, 0, 0, 0.3);}
        .success { background: rgba(0, 255, 0, 0.3);}
    </style>
</head>
<body>


<script>
    const call = (url) => {

        fetch(url, {
            redirect: "manual",
            method: "POST"
        }).then(async response => {
            const className = (response.redirected) ? 'success' : 'error';
            const content = await response.text();
            document.querySelector('body').innerHTML += `<div class='${className}'>${url}<hr /><b>[${response.status}]</b> ${content}</div>`;
        });
    };

    call('target/endpoint');
    call('target/subfolder');

</script>

</body>
</html>

Open http://localhost/ ... testscript.html in browser and see: the call on target/subfolder changed it's method.在浏览器中打开http://localhost/ ... testscript.html并看到: target/subfolder上的调用改变了它的方法。 Also when you look at network tab in browser console you can see, that there was 301 forwarding.此外,当您查看浏览器控制台中的网络选项卡时,您会看到有 301 转发。 Why?为什么? And more important: How can I circumvent this.更重要的是:我怎样才能规避这一点。 My desired behavior is a rewrite without any changing in terms of method to the index.php in disregard of the existence of a folder with that name.我想要的行为是重写而不改变index.php的方法,而不管是否存在具有该名称的文件夹。 In other words: The same what happens when I call target/endpoint for target/subfolder .换句话说:当我为target/subfolder调用target/endpoint时会发生同样的情况。

I know about -d (and -f ) naturally, but that can be used - as far as I know - to to control behavior depending if file or folder exists - so I would assume without these flags the existence of the directory should not play any role, but it does, apparently.我自然知道-d (和-f ),但是可以使用 - 据我所知 - 根据文件或文件夹是否存在来控制行为 - 所以我假设没有这些标志,目录的存在不应该播放任何角色,但显然确实如此。

So first the reason of 301 :所以首先是301的原因:

  1. You're requesting a URI /target/subfolder which actually points to an directory on your site.您正在请求一个 URI /target/subfolder ,它实际上指向您网站上的一个目录。
  2. Since your URI is missing a trailing slash.由于您的 URI 缺少尾部斜杠。 mod_dir module of Apache comes into action and sends a 301 redirect by making it /target/subfolder/ . Apache 的mod_dir模块生效并通过将其设置为/target/subfolder/来发送301重定向。 This is done for security reasons to avoid your directory content being exposed on the web.这样做是出于安全原因,以避免您的目录内容暴露在网络上。

To fix, you can just use call with a trailing slash everywhere it points to a directory:要修复,您可以在指向目录的任何地方使用带有斜杠的call

call('target/subfolder/');

But this cumbersome because front end developers won't always know these specific cases.但这很麻烦,因为前端开发人员并不总是知道这些特定情况。

You can use a generic rule in your .htaccess or Apache config to add this trailing slash:您可以在 .htaccess 或 Apache 配置中使用通用规则来添加此尾部斜杠:

# add a trailing slash to directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule [^/]$ %{REQUEST_URI}/ [L,R=301,NE]

Make sure this rule is just below RewriteEngine On line and before your existing rules.确保此规则位于RewriteEngine On线下方且位于现有规则之前。

With the help of @anuhava I found the right way to solve this.在@anuhava 的帮助下,我找到了解决这个问题的正确方法。 His answer alone did not help, but led me to this thread , and I found out the solution war actually to disable automatic trailing slash in .htaccess :单独他的回答没有帮助,但将我带到了这个线程,我发现了解决方案战争实际上禁用了.htaccess自动尾随斜杠:

DirectorySlash Off

I had the additional (not mentioned before) requirement to define a exception directory which should be accessible directly.我有一个额外的(之前没有提到的)要求来定义一个应该可以直接访问的异常目录。 To make this possible as well, I needed the rule of @anuhava and I endet up with this .htaccess :为了使这成为可能,我需要@anuhava 的规则,我最终得到了这个.htaccess

DirectorySlash Off

Options +FollowSymLinks
Options -Indexes

RewriteEngine on

# add a trailing slash to directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule [^/]$ %{REQUEST_URI}/ [L]

# rewrite all calls excpet for exception_dir to index.php
RewriteCond %{REQUEST_URI} !/target/exception_dir
RewriteRule ^ index.php [QSA,L]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM