【问题标题】:Apache 2 rewrite rule is incorrectly changing the URLApache 2 重写规则错误地更改了 URL
【发布时间】:2017-07-30 10:02:59
【问题描述】:

请注意,这是对我之前提出的问题的重复。然而它最初的措辞很糟糕,并且被编辑了很多次,几乎变成了一个不同的问题,因此我决定提出一个新问题

文件

我有以下文件:

example.dev.conf 虚拟主机文件

这是一个非常简单的 vhost 文件:

<VirtualHost *:80>
        ServerName example.dev
        ServerAlias *.example.dev
        DocumentRoot /var/www/example

        <Directory /var/www/example >
                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>

<VirtualHost *:443>
        ServerName example.dev
        ServerAlias *.example.dev 
        DocumentRoot /var/www/example

        <Directory /var/www/example >
                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>

.htaccess

以下文件将所有 url 重定向到 https 等效项,除非它以 /api/ 开头。

它还应该确保所有路由都使用index.php 控制器。

RewriteEngine On

# Handle SSL
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} !^/api/ [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R]

# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

问题

这在大多数情况下都有效。

例如http://example.dev/blah 重定向到https://example.dev/blah

但是,对于以/api/ 开头的任何内容,它似乎都无法按预期工作。

例如:http://example.dev/api/blah 重定向到 https://example.dev/index.php,而它根本不应该进行重定向!

请注意,如果我删除 htaccess 文件的最后 3 行,重定向工作正常。

如何复制?

我发现有些人发现很难复制我的问题,并坚持在某处存在其他规则。因此,我决定尽我所能展示我的所有步骤,以便很容易复制:

vagrant init ubuntu/trusty64
vagrant up --provider virtualbox
vagrant ssh

sudo apt-get install apache2
sudo a2enmod rewrite

cd /etc/apache2/sites-available/
sudo touch example.dev.conf   
# copy contents of the example.dev.conf vhost file as seen above
sudo a2ensite example.dev.conf

cd /var/www/
sudo mkdir example
sudo chown -R www-data:www-data /var/www/example
sudo chmod 777 -R /var/www/example
cd example
echo "test" > index.php
touch .htaccess
# Copy contents of .htaccess file as seen above

sudo chown -R www-data:www-data /var/www/example
sudo chmod 777 -R /var/www/example

sudo service apache2 restart

【问题讨论】:

    标签: apache .htaccess mod-rewrite apache2.4


    【解决方案1】:

    我想我设法解决了这个问题。

    读完后:http://httpd.apache.org/docs/current/rewrite/flags.html#flag_l 原来最后一个标志并不一定意味着没有使用进一步的规则。

    例如,对于以下文件:

    RewriteEngine On
    
    # Handle SSL
    RewriteCond %{HTTPS} off
    RewriteCond %{REQUEST_URI} !^/api/ [NC]
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=308]
    
    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
    
    • /api/blah 被重写为 index.php
    • 然后它再次遍历所有规则。根据文档,发生这种情况的原因如下:

    处理重写的请求时,可能会再次遇到 .htaccess 文件或部分,因此可能会从头开始再次运行规则集。最常见的情况是,如果其中一条规则导致重定向(内部或外部)导致请求过程重新开始。

    • index.php 然后匹配第一条规则,从而创建到 https://example.dev/index.php 的重定向。

    解决方案是改用END flag(正如@anubhava 提到的,仅在 Apache 2.4+ 中受支持),以防止规则被重新处理。

    所以解决方法如下:

    RewriteEngine On
    
    # Handle SSL
    RewriteCond %{HTTPS} off
    RewriteCond %{REQUEST_URI} !^/api/ [NC]
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R]
    
    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [END]
    

    现在我去http://example.dev/api/blah:

    • 改写为index.php
    • 因为它有END 规则,所以不再处理这些规则!
    • 因此,http://example.dev/api/blah 保持原样并根据需要使用 index.php 控制器。

    感谢 @Olaf Dietsche 让我走上正轨并建议 308 重定向!

    【讨论】:

      【解决方案2】:

      其他两个答案都很好,但我发布另一个答案是出于以下 3 个原因:

      1. Answer from Olaf:除了用户在浏览器中直接输入http://domain.con/index.php外,它适用于所有情况。根据要求,除了以/api/ 开头的任何内容都应重定向到`https://
      2. Answer from Yahya: END 标志仅在 Apache 2.4+ 中受支持,因此此答案不适用于 Apache 2.2 上面临类似答案的人。
      3. POST 请求应使用 307/308 重定向,否则帖子数据将丢失(请参阅下面 Olaf 的有用评论)。

      为了使其适用于这两种情况,我建议使用THE_REQUEST 而不是REQUEST_URI,因为在最后一次规则执行后REQUEST_URI 更改为/index.php

      RewriteEngine On
      
      # Handle SSL
      RewriteCond %{HTTPS} off
      RewriteCond %{THE_REQUEST} !\s/+api/ [NC]
      RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=308,NE]
      
      # Handle Front Controller...
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteRule ^ index.php [L]
      

      【讨论】:

      • 如果我也想将post数据转发到HTTPS怎么办?
      • 为此,您有状态代码307/308。虽然这些仅适用于 HTTP/1.1
      • 非常感谢 @OlafDietsche 回答 Yahya 的这个问题。是的,将 R=301 更改为 R=308 以保留 POST 数据。大多数现代浏览器都支持HTTP/1.1
      【解决方案3】:

      /api/ 请求被重定向到https://example.dev/index.php 的原因很可能是以下步骤

      • /api/...重写为index.php
      • index.php 重定向到https://...

      要了解为什么会发生这种情况,请查看 Ruleset Processing 的工作原理。规则是按顺序尝试的,但是当发生重写时(图中的“URI 已更改?”),Apache 会再次从生成的 URI 开始,直到不再发生重写。


      为避免/api/ 重定向到https://...,您还必须排除第一条规则的index.php,例如

      RewriteCond %{HTTPS} off
      RewriteCond %{REQUEST_URI} !^/api/ [NC]
      RewriteCond %{REQUEST_URI} !^/index\.php$ [NC]
      RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R]
      

      【讨论】:

      • 但是排除第二条规则的api意味着如果我有http://example.dev/api/blah,它将不再使用我的index.php控制器。相反,它会查找名为 api/blah 的物理文件,这通常会给出 404 错误
      • 好的,所以你想从api重写为index.php
      • 所有 url 仍应使用 index.php 控制器。我唯一想要的是将 https 设置为以 /api/ 开头的任何内容都是可选的,而其他所有内容都是必需的。
      • 好吧,哇,我想你明白了。但我仍然对为什么会发生这种情况感到困惑。当/api/... 被重写为index.php 时,这是最后一条规则! index.php 不应重定向到 https://...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-13
      • 1970-01-01
      • 1970-01-01
      • 2014-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多