当 url 包含 HTTPS 时,.htaccess 无法重写
.htaccess fails to rewrite when url contains HTTPS
我已经坐了很长时间了,无法理解它。
基本上,我最终要寻找的功能类似于以下内容:
http://example.com
重定向到 https://example.com
- 当前工作
并为
http://*.example.com
和 https://*.example.com
重定向到 https://example.com/*
- 无效。目前,所有子域都被重定向到主域,没有各自的文件夹路径。此外,当转到 https://test.example.com
时,它根本不会重定向。
这是来自 sites-enabled
的 example.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:
- 将重写从
.htaccess
移动到 example.com.conf
(代码在 post 中更新)。
- 为端口 80 添加了 VHost,本质上只是将所有流量重定向到 HTTPS。我发现重定向有效,但它不会在重定向中包含和子域,因此
http://test.example.com
只会导致 https://example.com
.
- 运行
a2enmod rewrite
,因为显然它没有启用。
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 都会被浏览器缓存)。
我已经坐了很长时间了,无法理解它。
基本上,我最终要寻找的功能类似于以下内容:
http://example.com
重定向到 https://example.com
- 当前工作
并为
http://*.example.com
和 https://*.example.com
重定向到 https://example.com/*
- 无效。目前,所有子域都被重定向到主域,没有各自的文件夹路径。此外,当转到 https://test.example.com
时,它根本不会重定向。
这是来自 sites-enabled
的 example.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:
- 将重写从
.htaccess
移动到example.com.conf
(代码在 post 中更新)。 - 为端口 80 添加了 VHost,本质上只是将所有流量重定向到 HTTPS。我发现重定向有效,但它不会在重定向中包含和子域,因此
http://test.example.com
只会导致https://example.com
. - 运行
a2enmod rewrite
,因为显然它没有启用。
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 都会被浏览器缓存)。