使用.htaccess和mod_rewrite强制SSL / https


Answers:


438

对于Apache,您可以使用mod_ssl强制使用SSL SSLRequireSSL Directive

除非为当前连接启用了SSL上的HTTP(即HTTPS),否则该指令禁止访问。这在启用SSL的虚拟主机或目录中非常方便,可防止配置错误暴露应受保护的内容。使用此指令时,将拒绝所有未使用SSL的请求。

不过,这不会重定向到https。要重定向,请mod_rewrite在.htaccess文件中尝试以下操作

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

或在

如果您的提供者禁用了.htaccess(也可能自您提出请求以来就不太可能了),您也可以从PHP中解决此问题。

if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
    if(!headers_sent()) {
        header("Status: 301 Moved Permanently");
        header(sprintf(
            'Location: https://%s%s',
            $_SERVER['HTTP_HOST'],
            $_SERVER['REQUEST_URI']
        ));
        exit();
    }
}

1
在我们的情况下,这非常好,因为我们目前混合使用HTTP和https流量。对于我们的管理区域,我们只是弹出.htaccess脚本,同时保留网站的其余部分http。
Michael J. Calkins 2014年

1
当我使用mod_rewrite方法时,会发送到https,但出现“页面无法正确重定向”错误。
捷克学2014年

11
后续:如果遇到类似的问题,请检查服务器和HTTP变量。如果服务器使用代理,则可能要使用%{HTTP:X-Forwarded-Proto}%{HTTP:X-Real-Port}变量来检查SSL是否已打开。
捷克学2014年

8
如果在代理后面运行的服务器(CloudFlareOpenshift)上遇到重定向循环,请参阅此答案以获取适用于这种情况的解决方案。
raphinesse

4
@GTodorov-删除空间为我破坏了重写器。根据我对重写器的(有限的)知识,语法为RewriteRule <input-pattern> <output-url>。因此,空间必须存在,并且单身^只说“匹配所有输入URL”。
Sphinxxx '16

54

我找到了一种mod_rewrite适用于代理服务器和非代理服务器的解决方案。

如果您正在使用CloudFlare,AWS Elastic Load Balancing,Heroku,OpenShift或任何其他Cloud / PaaS解决方案,并且遇到使用常规HTTPS重定向的重定向循环,请尝试以下代码段。

RewriteEngine On

# If we receive a forwarded http request from a proxy...
RewriteCond %{HTTP:X-Forwarded-Proto} =http [OR]

# ...or just a plain old http request directly from the client
RewriteCond %{HTTP:X-Forwarded-Proto} =""
RewriteCond %{HTTPS} !=on

# Redirect to https version
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

这太棒了!但是也许需要添加发布条件?RewriteCond%{REQUEST_METHOD}!^ POST $
Aleksej

@Aleksej是的,这破坏了未加密的POST请求。但是我认为这是一件好事。这样,人们会注意到他们在做错事。我想最好的办法是将它们重定向到一个页面,该页面告知他们如何正确使用您的服务。
raphinesse

@raphinesse你知道这个问题的答案吗:stackoverflow.com/questions/51951082/…–
RRN

36

PHP解决方案

直接从Gordon非常全面的答案中借用,我注意到您的问题提到在强制HTTPS / SSL连接时特定于页面。

function forceHTTPS(){
  $httpsURL = 'https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
  if( count( $_POST )>0 )
    die( 'Page should be accessed with HTTPS, but a POST Submission has been sent here. Adjust the form to point to '.$httpsURL );
  if( !isset( $_SERVER['HTTPS'] ) || $_SERVER['HTTPS']!=='on' ){
    if( !headers_sent() ){
      header( "Status: 301 Moved Permanently" );
      header( "Location: $httpsURL" );
      exit();
    }else{
      die( '<script type="javascript">document.location.href="'.$httpsURL.'";</script>' );
    }
  }
}

然后,在要强制通过PHP连接的这些页面的顶部附近,可以require()包含一个包含此(和任何其他)自定义函数的集中文件,然后简单地运行该forceHTTPS()函数。

HTACCESS / mod_rewrite解决方案

我还没有亲自实现过这种解决方案(为简单起见,我倾向于使用PHP解决方案,就像上面的解决方案一样),但是以下至少是一个好的开始。

RewriteEngine on

# Check for POST Submission
RewriteCond %{REQUEST_METHOD} !^POST$

# Forcing HTTPS
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{SERVER_PORT} 80
# Pages to Apply
RewriteCond %{REQUEST_URI} ^something_secure [OR]
RewriteCond %{REQUEST_URI} ^something_else_secure
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

# Forcing HTTP
RewriteCond %{HTTPS} =on [OR]
RewriteCond %{SERVER_PORT} 443
# Pages to Apply
RewriteCond %{REQUEST_URI} ^something_public [OR]
RewriteCond %{REQUEST_URI} ^something_else_public
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

出于好奇,为什么RewriteCond %{REQUEST_METHOD} !^POST$
depoulo

12
因为POST参数未保留在重定向中。如果要确保所有POST提交都是安全的,则可以省略该行(任何不安全的POST提交都将被忽略)。
路加·史蒂文森

@Lucanos-如何编写一个POST时不强制重定向到http或https的RewriteCond?我的.htaccess会在某些特定页面上强制使用HTTPS,然后在其余页面上强制使用HTTP。但是,在HTTPS页面上,有一些表单提交到我的Web根目录。该表格将操作网址指定为HTTPS。但是,由于Web根目录不是指定用于强制HTTPS的页面之一,因此我的.htaccess会强制进行重定向-这意味着POST变量会丢失。如何防止POST上的重定向?
StackOverflowNewbie 2014年

1
@StackOverflowNewbie:该行RewriteCond %{REQUEST_METHOD} !^POST$应防止POST提交受到这些重定向的影响。
卢克·史蒂文森

我喜欢这个PHP版本。考虑到POST会更好,并且如果已经发送了标头,则处理会更好。
TheStoryCoder

10

基于Mod-rewrite的解决方案:

在htaccess中使用以下代码会自动将所有http请求转发到https。

RewriteEngine on

RewriteCond %{HTTPS}::%{HTTP_HOST} ^off::(?:www\.)?(.+)$
RewriteRule ^ https://www.%1%{REQUEST_URI} [NE,L,R]

这会将您的非wwwwww http请求重定向到https的www版本。

另一个解决方案(Apache 2.4 *)

RewriteEngine on

RewriteCond %{REQUEST_SCHEME}::%{HTTP_HOST} ^http::(?:www\.)?(.+)$
RewriteRule ^ https://www.%1%{REQUEST_URI} [NE,L,R]

自2.4版本起,已将%{REQUEST_SCHEME}变量添加到mod-rewrite中,这不适用于较低版本的apache。


9

我只想指出,在跨目录深度使用多个.htaccess文件时,Apache具有最差的继承规则。两个主要陷阱:

  • 默认情况下,仅执行最深.htaccess文件中包含的规则。您必须指定RewriteOptions InheritDownBefore指令(或类似指令)才能进行更改。(请参阅问题)
  • 该模式将应用于相对于子目录的文件路径,而不是包含具有给定规则的.htaccess文件的上层目录。(请参见讨论)

这意味着对建议的全球性解决方案的Apache维基没有,如果你在子目录中使用任何其他的.htaccess文件。我写了一个修改后的版本,它可以:

RewriteEngine On
# This will enable the Rewrite capabilities

RewriteOptions InheritDownBefore
# This prevents the rule from being overrided by .htaccess files in subdirectories.

RewriteCond %{HTTPS} !=on
# This checks to make sure the connection is not already HTTPS

RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [QSA,R,L]
# This rule will redirect users from their original location, to the same location but using HTTPS.
# i.e.  http://www.example.com/foo/ to https://www.example.com/foo/

0

简单易用,只需添加以下内容

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

这几乎是整个Internet上的人们告诉您强制执行https的操作。但是,每次这样做,我的整个网站都会被禁止或您无权查看。诸如此类...我在做什么错?我只想知道您是否有任何想法,然后再对此发表问题。
ThN

我有一个类似的问题,我不得不将规则应用于https.conf文件。请参阅下面的答案。
Risteard

-1

该代码对我有用

RewriteEngine On
RewriteBase /
RewriteCond %{HTTP:X-HTTPS} !1
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]


-1

只需使用Apache .htaccess强制使用SSL

SSLOptions +StrictRequire
SSLRequireSSL

对于重定向以上答案是正确的


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.