Apache .htaccess redirect to HTTPS before asking for user authentication
Asked Answered
M

9

31

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?

Mausoleum answered 22/4, 2012 at 10:16 Comment(1)
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).Thrower
F
17

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
Festoonery answered 11/4, 2013 at 3:46 Comment(4)
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.Giffard
SERVER_PORT is not an option available in SetEnvIf directive as per the documentation at httpd.apache.org/docs/2.2/mod/mod_setenvif.html#setenvifTahsildar
Plus at least in my case it stills asks for authentication twice.Kris
@ChrisHeald could you post your solution as an answer? Because this one here does not work for me.Edmondo
I
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>
Imagery answered 2/10, 2014 at 5:45 Comment(6)
Definitely don't do this. Because of directive merging order (<If> is merged last), authenticated users will have access to any file or directory previously blocked with either <Directory> or <File>! For example, on ordinary server setups the .htaccess file will be granted access for valid users.Frick
@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 <file> or <directory> directives. What do you think?Imagery
I didn't test this setup, but it seems ok from the documentation, so I'll take your word out of it.Frick
@Molomby, please remove the inline comment though: Comments may not be included on the same line as a configuration directive.Kris
Can there be <IfVersion >= 2.4> for the <If "%{HTTPS} == 'on'">? I guess SSLRequireSSL could use <IfVersion < 2.4> too.Kris
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?Nonbeliever
F
17

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
Festoonery answered 11/4, 2013 at 3:46 Comment(4)
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.Giffard
SERVER_PORT is not an option available in SetEnvIf directive as per the documentation at httpd.apache.org/docs/2.2/mod/mod_setenvif.html#setenvifTahsildar
Plus at least in my case it stills asks for authentication twice.Kris
@ChrisHeald could you post your solution as an answer? Because this one here does not work for me.Edmondo
P
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.

Pigeon answered 10/5, 2015 at 7:56 Comment(4)
Please don't add "thank you" as an answer. Once you have sufficient reputation, you will be able to vote up questions and answers that you found helpful.Undercoat
That is odd. According to the documentation of Apache 2.2 for mod_rewrite (for Apache 1.3 and later) and mod_version (for Apache 2.0.56 and later) both directives should be valid, assuming that both modules are installed.Narcissus
@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!Calci
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.Emelyemelyne
E
4

The checked solution https://mcmap.net/q/461307/-apache-htaccess-redirect-to-https-before-asking-for-user-authentication 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.

Edmondo answered 19/4, 2017 at 9:6 Comment(1)
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?Glottal
B
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:[email protected]/~WEBUSER/
### @link https://mcmap.net/q/435518/-how-to-logout-when-using-htaccess-and-htpasswd-authentication-duplicate

### 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
Biegel answered 17/2, 2015 at 11:55 Comment(1)
Why do you need SSLRequire %{HTTP_HOST} eq "DOMAIN.TLD"? For which case does this help? By the way: SSLRequire is depreciated httpd.apache.org/docs/current/mod/mod_ssl.html#sslrequireEdmondo
I
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.
Impasto answered 17/12, 2015 at 9:41 Comment(0)
S
2

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>
Superdreadnought answered 8/2, 2018 at 6:42 Comment(1)
Yes only one that worked for me, you need to check both https and www are on before require valid-user.Trometer
D
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)

Declarant answered 22/1, 2019 at 18:16 Comment(0)
N
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

Narcissus answered 19/2, 2015 at 5:36 Comment(3)
Nice thought, but it fails (500 error) because even with <IfVersion your script can't contain unknown commands like Configuration Sections (in v2.2). That's how it is in most if not all programming languages. This is where something like PHP's function_exists could have been handy.Kris
@Kris 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.)Narcissus
I guess not all, just a lot of them. Thanks for the clarification!Kris

© 2022 - 2024 — McMap. All rights reserved.