当 url 包含 HTTPS 时,.htaccess 无法重写

.htaccess fails to rewrite when url contains HTTPS

我已经坐了很长时间了,无法理解它。

基本上,我最终要寻找的功能类似于以下内容:

http://example.com 重定向到 https://example.com - 当前工作

并为

http://*.example.comhttps://*.example.com 重定向到 https://example.com/* - 无效。目前,所有子域都被重定向到主域,没有各自的文件夹路径。此外,当转到 https://test.example.com 时,它根本不会重定向。

这是来自 sites-enabledexample.com.conf

<IfModule mod_ssl.c>

    <VirtualHost *:80>
        ServerName example.com
        ServerAlias www.example.com
#       Redirect permanent / https://example.com/

        <Directory /var/www/example.com/public_html>
            Options FollowSymLinks
            AllowOverride All
            Require all granted
        </Directory>
        
        <IfModule mod_rewrite.c>
            # Redirect all traffic to HTTPS
            RewriteCond %{HTTPS} !on
            RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [QSA,R=301,L]
        </IfModule>


    </VirtualHost>

    <VirtualHost _default_:443>
        ServerAdmin webmaster@localhost
        ServerName example.com
        ServerAlias www.example.com
        DocumentRoot /var/www/example.com/public_html

        <Directory /var/www/example.com/public_html>
            Options FollowSymLinks
            AllowOverride All
            Require all granted
        </Directory>

        <IfModule mod_rewrite.c>
            # Remove www
            RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
            RewriteRule ^(.*)$ http://%1%{REQUEST_URI} [QSA,R=301,L]

            RewriteCond %{HTTP_HOST} !^(www)\. [NC]
            RewriteCond %{HTTP_HOST} ^(.*)\.example\.com [NC]
            RewriteRule ^(.*)$ https://example.com/%1/ [R=301,L]
        </IfModule>

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        SSLEngine on
        SSLCertificateFile  /etc/ssl/civrpssl/example.com.crt
        SSLCertificateKeyFile /etc/ssl/civrpssl/example.com.key
        SSLCACertificateFile /etc/ssl/civrpssl/ca-example.com.crt

        <FilesMatch "\.(cgi|shtml|phtml|php)$">
                SSLOptions +StdEnvVars
        </FilesMatch>
        <Directory /usr/lib/cgi-bin>
                SSLOptions +StdEnvVars
        </Directory>

    </VirtualHost>
</IfModule>

最后,这是来自 example.com/public_html/.htaccess

removed, see edit #2.

我希望这是一个我没有看到的相当明显的问题。否则,请有人告诉我应该从哪里开始寻找问题?我假设 HTTPS 重定向会干扰 .htaccess 文件中的子域重定向,但我不知道如何换一种方式。

编辑 #1: 我刚刚注意到 www.example.com 抛出错误“URL 未找到”而不是重定向到 https://example.com .为什么它没有被 .htaccess 中的第一次重写捕获?

编辑#2:

ServerName example.com
ServerAlias www.example.com

<VirtualHost> 个容器都不接受对任何子域的请求,www 除外。如果 vHost 是服务器配置中第一个定义的 vHost,它可能仍会捕获其他子域(实际上,any 主机名)。要捕获“任何”子域,您需要另一个形式为 ServerAlias 的“通配符”:

ServerAlias *.example.com

删除 <IfModule mod_rewrite.c> 包装器 - 它们不是必需的。如果 mod_rewrite 不可用,他们只会 屏蔽 错误。 <IfModule> 包装器仅应在这些指令完全 可选 并且配置要移植到 mod_rewrite 不可用的其他系统时使用。

更新: 您还缺少 vHost 中的 RewriteEngine On 指令。除非重写引擎已经在服务器配置的其他地方启用(不太可能),否则这些 mod_rewrite 指令将被完全跳过。

在 HTTP vHost(端口 80)中...

       # Redirect all traffic to HTTPS
       RewriteCond %{HTTPS} !on
       RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [QSA,R=301,L]

没有必要在 vHost 中检查端口 80 的 HTTPS,因为它始终是 off。如果您想直接从子域重定向到子目录,则需要将上面的内容更改为:

        # Redirect HTTP subdomains to HTTPS subdirectory (excluding www)
        RewriteCond %{HTTP_HOST} !^www\. [NC]
        RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com [NC]
        RewriteRule ^/(.*) https://example.com/%1/ [R=301,L]

        # Redirect HTTP to HTTPS
        RewriteRule ^/(.*) https://example.com/ [R=301,L]

但是,如果您打算在将来实施 HSTS,那么您将需要首先在同一主机上重定向到 HTTPS,然后仅针对端口 443 发出第二次重定向到 vHost 中的子目录。 (使用 Redirect 指令从 HTTP 重定向到 HTTPS - 目前已注释掉 - 虽然这可能需要为每个子域提供一个单独的(最小)vHost,这可能是不可取的。)

^/(.*) - 注意在 RewriteRule 模式 中捕获子模式外添加了一个斜杠前缀。在 virtualhost 上下文中,完整的 URL-path 与 RewriteRule 模式 匹配。尾部 $ 不是必需的,因为默认情况下正则表达式是贪婪的。

另请注意正则表达式 ([^.]+)(而不是 (.*)),它仅专门匹配子域(无点)。

在您的 HTTPS 虚拟主机(端口 443)中,您有:

       # Remove www
       RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
       RewriteRule ^(.*)$ http://%1%{REQUEST_URI} [QSA,R=301,L]

您正在重定向回 HTTP(尽管这似乎是您在编辑后引入的错误?)。

如果您使用 REQUEST_URI 变量,则无需捕获 RewriteRule 模式

然后将相同的编辑应用到子域到子目录重定向,如上所述。

总结

综合以上几点,我们得到类似的结果:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    ServerAlias *.example.com

    #Redirect permanent / https://example.com/

    RewriteEngine On
    
    # Redirect HTTP subdomains to HTTPS subdirectory (excluding www)
    RewriteCond %{HTTP_HOST} !^www\. [NC]
    RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com [NC]
    RewriteRule ^/(.*) https://example.com/%1/ [R=301,L]

    # Redirect HTTP to HTTPS (www and domain apex)
    RewriteRule ^/(.*) https://example.com/ [R=301,L]
</VirtualHost>

<VirtualHost *:443>
    ServerAdmin webmaster@localhost
    ServerName example.com
    ServerAlias www.example.com
    ServerAlias *.example.com
    DocumentRoot /var/www/example.com/public_html

    <Directory /var/www/example.com/public_html>
        Options FollowSymLinks
        # Set "AllowOverride None" to disable .htaccess altogether
        AllowOverride All
        Require all granted
    </Directory>

    RewriteEngine On

    # Remove www
    RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
    RewriteRule ^ https://%1%{REQUEST_URI} [QSA,R=301,L]

    # Redirect other subdomains to subdirectory
    RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com [NC]
    RewriteRule ^/(.*) https://example.com/%1/ [R=301,L]

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    SSLEngine on
    SSLCertificateFile  /etc/ssl/civrpssl/example.com.crt
    SSLCertificateKeyFile /etc/ssl/civrpssl/example.com.key
    SSLCACertificateFile /etc/ssl/civrpssl/ca-example.com.crt

    <FilesMatch "\.(cgi|shtml|phtml|php)$">
            SSLOptions +StdEnvVars
    </FilesMatch>
    <Directory /usr/lib/cgi-bin>
            SSLOptions +StdEnvVars
    </Directory>

</VirtualHost>

您应该首先使用 302(临时)重定向进行测试,以避免潜在的缓存问题。

并在测试前清除浏览器缓存(任何错误的 301 都会被浏览器缓存)。