30

This is my .htaccess:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

AuthUserFile /etc/hi
AuthName "hi"
AuthType Basic
require valid-user

It asks for user authentication using http, meaning that password will be sent in plain text. It will than redirect to the https version and ask the password again.

How can i fix it?

Qiau
  • 5,976
  • 3
  • 29
  • 40
cedivad
  • 2,544
  • 6
  • 32
  • 41
  • 2
    You need to have HTTP and HTTPS sites declared in separate virtual host entries, so config for one will not interfere with another. When done, the rewrite rule should be in http one and auth in https one (This all assumes that you access site over HTTPS only). – LazyOne Apr 22 '12 at 15:31

9 Answers9

24

If you're running Apache 2.4 you can use configuration sections to solve this quite easily.

Eg...

# Redirect to HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

# Authenticate users only when using HTTPS
# Enable for <v2.4
 # SSLRequireSSL
 # ErrorDocument 403 /secure-folder/
# Enable for >v2.4
<If "%{HTTPS} == 'on'">
    AuthType Basic
    AuthName "Special things"
    AuthUserFile /etc/blah.htpasswd
    # Prevent this 'Require' directive from overriding any merged previously
   <IfVersion >= 2.4>
      AuthMerging And
   </IfVersion>
    Require valid-user
# Enable for >v2.4
</If>
LWC
  • 1,084
  • 1
  • 10
  • 28
Molomby
  • 5,859
  • 2
  • 34
  • 27
  • 6
    *Definitely don't do this.* Because of directive merging order (`` is merged last), authenticated users will have access to any file or directory previously blocked with either `` or ``! For example, on ordinary server setups the `.htaccess` file will be granted access for valid users. – alecov Nov 08 '14 at 17:56
  • 4
    @Alek, I've added an 'AuthMerging' directive that I believe addresses the issue you raised. So all visitors are still redirected to HTTPS but any HTTPS requests require the visitor to be authenticated *in addition* to existing restrictions added by previously merged or directives. What do you think? – Molomby Dec 05 '14 at 02:16
  • 3
    I didn't test this setup, but it seems ok from the documentation, so I'll take your word out of it. – alecov Mar 07 '15 at 14:51
  • 2
    @Molomby, please remove the inline comment though: [Comments may **not** be included on the same line as a configuration directive.](http://stackoverflow.com/a/22107065/4829915) – LWC Mar 22 '16 at 21:02
  • Can there be = 2.4> for the ? I guess SSLRequireSSL could use too. – LWC Feb 23 '20 at 14:47
  • I've tested the accepted answer from Ben as well as this answer on Apache 2.4. Both works for me. But since this answer doesn't need an env variable it should be better. Am I right? – Alexander Behling Feb 24 '21 at 08:39
16

I get around it this way. Just allow Non-SSL since it will be redirected then require auth once on SSL...

SetEnvIf %{SERVER_PORT} ^80$ IS_NON_SSL

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

AuthUserFile /etc/hi
AuthName "hi"
AuthType Basic
require valid-user
Allow from env=IS_NON_SSL
Ben
  • 242
  • 4
  • 3
  • 3
    Elegant solution to a not entirely obvious problem. I ended up using `Allow from env=!HTTPS` where HTTPS is set on SSL requests, but the same concept applies. Thanks. – Chris Heald May 25 '13 at 07:04
  • 2
    SERVER_PORT is not an option available in SetEnvIf directive as per the documentation at http://httpd.apache.org/docs/2.2/mod/mod_setenvif.html#setenvif – Alagappan Ramu Mar 20 '14 at 19:18
  • 4
    Plus at least in my case it stills asks for authentication twice. – LWC Mar 22 '16 at 20:55
  • @ChrisHeald could you post your solution as an answer? Because this one here does not work for me. – Adam Apr 18 '17 at 13:41
12

Thank you very much, Istador!

My Apache is of version 2.2 (Synology NAS DSM 5.1) so these two do not work on it:

RewriteOptions Inherit
IfVersion

After taking them (and the section of version >= 2.4) out. The whole thing began to work for me.

There are a lot suggestions out there for this topic, I spent two days to try them out.

But only this one works for me.

Here's what I did:

RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

AuthType Basic
AuthName "private area"
AuthUserFile /path/to/file/.htdigest

Order Deny,Allow
Deny from all
Satisfy Any
Allow from env=!HTTPS
Require valid-user

So it's verified to work on Apache 2.2, Synology DSM 5.1.

Bojan Kseneman
  • 15,488
  • 2
  • 54
  • 59
MXW
  • 129
  • 1
  • 2
  • 1
    Please don't add "thank you" as an answer. Once you have sufficient [reputation](http://stackoverflow.com/help/whats-reputation), you will be able to [vote up questions and answers](http://stackoverflow.com/help/privileges/vote-up) that you found helpful. – Ilya May 10 '15 at 09:18
  • That is odd. According to the documentation of Apache 2.2 for [mod_rewrite](http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewriteoptions) (for Apache 1.3 and later) and [mod_version](http://httpd.apache.org/docs/2.2/mod/mod_version.html) (for Apache 2.0.56 and later) both directives should be valid, assuming that both modules are installed. – Istador Jun 28 '15 at 15:56
  • 2
    @Ilya. The 'thank you' is helpful, as the post relates to their specific post, which is helpful in providing context for a reader like me. Without it, a reader might assume it is directly addressing only the OP's post. Of course, they could have just done an '@Istador' to be more concise! – Patanjali Oct 18 '16 at 23:55
  • 2
    This doesn't seem to be working on the Debian-based version of 2.2 I have - the `Allow from env=!HTTPS` seems to have no effect, resulting in all pages bypassing the auth step. – fooquency Jul 15 '18 at 17:03
4

The checked solution https://stackoverflow.com/a/15940387/2311074 does work on Firefox on Ubuntu 16.04, but it does not work on Firefox on Win 7.

If you want to protect your folder https://yourdomain.com/securefolder then you need to create in that folder a .htaccess with the following content:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

SSLRequireSSL
ErrorDocument 403 https://yourdomain.com/securefolder
AuthType Basic
AuthName "Admin"
AuthUserFile /outside/your/www/folder/.htpasswd
Require user admin Admin

The way it works is that when you are calling the website through http:// instead of https:// it will redirect you to the error page. The trick is to use the correct link with the https:// as your default error page.

Community
  • 1
  • 1
Adam
  • 25,960
  • 22
  • 158
  • 247
  • this is the only one that worked for me, I used `SSLRequireSSL ErrorDocument 403 https://%{HTTP_HOST} ` But I'm a bit concerned. Is that a proper way to redirect? I mean doesn't that 403 affect google in some nasty way? – brett Dec 10 '19 at 23:16
2

Our client's webapp is installed in his webuser directory. Authorisation is handled before mod_rewrite rules (https://serverfault.com/a/443185/253111), and we could not get the accepted answer to work, so mod_rewrite seemed not an option.

Eventually we explicitly required SSL and used the webapp's root over HTTPS as 403 and 404 error documents. So when one visits any page over HTTP (which is unauthorized, hence the 403) or a non existing page (404), he is being redirected to ie. https://DOMAIN.TLD/~WEBUSER/admin.

This is the .htaccess file with some extra info in the comments.

### INFO: Rewrites and redirects are handled after authorisation
### @link https://serverfault.com/a/443185/253111

### INFO: Log out of a HTPASSWD session
### This was not always possible, but Firefox and Chrome seem to end sessions
### when a new one is trying to be using ie.:
### https://logout:logout@DOMAIN.TLD/~WEBUSER/
### @link http://stackoverflow.com/a/1163884/328272

### FORCE SSL: Explicitly require the SSL certificate of a certain domain to
### disallow unsigned certificates, etc. ErrorDocument commands are used to
### redirect the user to an HTTPS URL.
### @link http://forum.powweb.com/showthread.php?t=61566
SSLOptions +StrictRequire
SSLRequireSSL
SSLRequire  %{HTTP_HOST} eq "DOMAIN.TLD"

### HTPASSWD AUTHENTICATION
AuthUserFile /var/www/vhosts/DOMAIN.TLD/web_users/WEBUSER/.htpasswd
AuthType Basic
AuthName "Hello"
Require valid-user

### ERROR DOCUMENTS: Redirect user in case of a 403 / 404.
ErrorDocument 403 https://DOMAIN.TLD/~WEBUSER/admin
ErrorDocument 404 https://DOMAIN.TLD/~WEBUSER/admin
Community
  • 1
  • 1
lmeurs
  • 16,111
  • 4
  • 27
  • 30
  • Why do you need `SSLRequire %{HTTP_HOST} eq "DOMAIN.TLD"`? For which case does this help? By the way: SSLRequire is depreciated https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslrequire – Adam Apr 19 '17 at 09:20
2

I'm running Apache 2.2 and none of the above solutions worked for me. I found a workaround for me here. Basically, you need to set SSLRequireSSL and use some script language in the ErrorDocument to forward users to HTTPS. Unfortunately, in my case this only works when accessing particular files on the server, it does NOT work if just the domain is provided. Here is what I did:

AuthType Basic
AuthName "Password Protected Area"
AuthUserFile /my/path/to/.htpasswd
#Require valid-user

<FilesMatch "(^(?!ssl.php).*)">
        SSLRequireSSL
        ErrorDocument 403 /ssl.php
        Require valid-user
</FilesMatch>

The regex in FileMatch tells apache to SSLRequireSSL for all files except ssl.php - and forward the user to ssl.php if he tries to access without SSL.

My ssl.php looks like this:

if(!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == "" || $_SERVER['HTTPS'] == "off")
{
        $redirect = "https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        header("HTTP/1.1 301 Moved Permanently");
        header("Location: $redirect");
        exit;
}

What now happens:

The last point is what I am not happy with, if someone has a solution for that, I'd be glad to hear about it. Things I tried to solve this issue:

  • Changed the regular expression to (^$)|(^(?!ssl.php).*) to explicitly also match empty strings. Did not work
  • Added a rewrite rule to rewrite an empty string to index.php. Does not work either.
user3205494
  • 297
  • 4
  • 10
2

Here is the only solution that worked in one of my configurations:

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

<If "%{SERVER_PORT} != '80'">
    AuthUserFile /etc/hi
    AuthName "hi"
    AuthType Basic
    require valid-user
</If>

Notably, this is for an Apache 2.4 over which I have no control (shared hosting). It seems that the %{HTTPS} variable is not defined on this config, and any solution based on SSLRequireSSL generated a 500 Internal Server Error.


(Side note: In case you prefer a 403 Forbidden instead of a 301 Permanent Redirect when serving HTTP requests, use RewriteRule ^(.*)$ - [F,L] instead)

Boris Dalstein
  • 7,015
  • 4
  • 30
  • 59
1

Molomby's solution works in 2.4 and higher, but doesn't work with the current Debian version 2.2.22.

Ben's / Chris Heald's solutions also didn't work for me in 2.2.22, but that was due to a different order/satisfy configuration. These settings have changed with 2.4 and the solution seems to be incompatible with 2.4 and above (the redirect works, but the browser is just displaying an unauthorized error without asking for credentials).

Here is a combination of both solutions that should work for versions below and above 2.4:

RewriteEngine on
RewriteOptions Inherit # rewrite rules from parent directories
RewriteCond %{HTTPS} off
RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

AuthType Digest
AuthName "private area"
AuthDigestProvider file
AuthUserFile /path/to/file/.htdigest

<IfVersion < 2.4>
    Order Deny,Allow
    Deny from all
    Satisfy Any # reset this to 'All' in custom <Files> and <Directory> directives that block access
    Allow from env=!HTTPS
    Require valid-user
</IfVersion>
<IfVersion >= 2.4>
    <If "%{HTTPS} == 'on'">
        AuthMerging And
        Require valid-user
    </If>
</IfVersion>

Requirements: mod_rewrite, mod_auth, mod_digest, mod_version

Istador
  • 163
  • 7
  • Nice thought, but it fails (500 error) because even with – LWC Aug 06 '17 at 21:09
  • @LWC 1.) Programming languages are a bad metaphor for your point. Counter example: invalid C code can be compiled, if a preprocessor removes the invalid sections. As an user I'd expect IfVersion to work similar without evaluating its content. 2.) I had this config in use from 2014-02 till 2014-09 with Apache 2.2.22. It might have been invalid and might have resulted in error log entries (I did not check), but it worked as expected and didn't responded with 5xx errors. (Maybe it only worked because of the Debian version, wich sometimes differentiates from the upstream version.) – Istador Aug 08 '17 at 19:14
  • I guess not all, just a lot of them. Thanks for the clarification! – LWC Aug 08 '17 at 23:29
1

None of the above worked for me, but this did. My only concern is if there are certain conditions whereby the auth is not triggered allowing someone access without the credentials. I'm not sure there are, but maybe you bright people may say otherwise.

This code redirects non-www to www and http to https, with .htaccess folder auth.

This is the contents of the htaccess file in the directory you want to protect:

RewriteEngine on
# ensure www.
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/foldername/$1 [L,R=301]
# ensure https
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} !=on [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}/foldername/$1 [L,R=301]

# Apache 2.4 If
<If "%{HTTPS} == 'on' && %{HTTP_HOST} =~ /www/">
AuthType Basic
AuthName "Protected folder"
AuthUserFile "/home/etc/.htpasswds/public_html/foldername/passwd"
require valid-user
</If>
MarkApp
  • 11
  • 2
  • Yes only one that worked for me, you need to check both https and www are on before require valid-user. – Douma Nov 05 '21 at 09:10