How to Redirecting HTTP to HTTPS, while terminating ssl via apache and varnish configuration

I’m using virtualmin with apache. Configured varnish, and want to terminate ssl with apache. Apache is listening two ports 443 & 8080 varnish listening port 80

Error that I’m facing:

http to https redirect issue in apache sites. When request is https://example.com no issue, but when request is http://example.com, it doesn’t redirects to https.

Here’s my apache ssl config:

SuexecUserGroup "#1010" "#1006"
ServerName example.com
ServerAlias www.example.com
ServerAlias mail.example.com
ServerAlias webmail.example.com
ServerAlias admin.example.com
ServerAlias autoconfig.example.com
ServerAlias autodiscover.example.com

DocumentRoot /home/biolink/public_html

ErrorLog /var/log/virtualmin/example.com_error_log
CustomLog /var/log/virtualmin/example.com_access_log combined

ScriptAlias /cgi-bin/ /home/biolink/cgi-bin/
ScriptAlias /awstats/ /home/biolink/cgi-bin/
ScriptAlias /AutoDiscover/AutoDiscover.xml /home/biolink/cgi-bin/autoconfig.cgi
ScriptAlias /Autodiscover/Autodiscover.xml /home/biolink/cgi-bin/autoconfig.cgi
ScriptAlias /autodiscover/autodiscover.xml /home/biolink/cgi-bin/autoconfig.cgi

DirectoryIndex index.html index.htm index.php index.php4 index.php5

<Directory /home/biolink/public_html>
    Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch +ExecCGI
    allow from all
    AllowOverride All Options=ExecCGI,Includes,IncludesNOEXEC,Indexes,MultiViews,SymLinksIfOwnerMatch
    Require all granted
    AddType application/x-httpd-php .php
    AddHandler fcgid-script .php
    AddHandler fcgid-script .php7.4
    AddHandler fcgid-script .php8.2
    FCGIWrapper /home/biolink/fcgi-bin/php7.4.fcgi .php
    FCGIWrapper /home/biolink/fcgi-bin/php7.4.fcgi .php7.4
    FCGIWrapper /home/biolink/fcgi-bin/php8.2.fcgi .php8.2
</Directory>

<Directory /home/biolink/cgi-bin>
    allow from all
    AllowOverride All Options=ExecCGI,Includes,IncludesNOEXEC,Indexes,MultiViews,SymLinksIfOwnerMatch
    Require all granted
</Directory>

RewriteEngine on
RewriteCond %{HTTP_HOST} =webmail.example.com
RewriteRule ^(?!/.well-known)(.*) https://example.com:20000/ [R]
RewriteCond %{HTTP_HOST} =admin.example.com
RewriteRule ^(?!/.well-known)(.*) https://example.com:10000/ [R]

SSLEngine on
SSLCertificateFile /home/biolink/ssl.cert
SSLCertificateKeyFile /home/biolink/ssl.key
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1

<Files awstats.pl>
    AuthName "example.com statistics"
    AuthType Basic
    AuthUserFile /home/biolink/.awstats-htpasswd
    require valid-user
</Files>

Alias /dav /home/biolink/public_html

<Location /dav>
    DAV on
    AuthType Basic
    AuthName "example.com"
    AuthUserFile /home/biolink/etc/dav.digest.passwd
    Require valid-user
    ForceType text/plain
    Satisfy All
    RemoveHandler .php
    RemoveHandler .php7.4

    RewriteEngine off
</Location>

<Location /git>
    DAV on
    AuthType Basic
    AuthName example.com
    AuthUserFile /home/biolink/etc/git.basic.passwd
    Require valid-user
    Satisfy All
    RedirectMatch ^/git$ http://example.com/git/gitweb.cgi
    RedirectMatch ^/git/$ http://example.com/git/gitweb.cgi

    RewriteEngine off
    AddHandler cgi-script .cgi
</Location>

SSLCACertificateFile /home/biolink/ssl.ca
RemoveHandler .php
RemoveHandler .php7.4
RemoveHandler .php8.2
IPCCommTimeout 2001

FcgidMaxRequestLen 1073741824
Redirect /mail/config-v1.1.xml /cgi-bin/autoconfig.cgi
Redirect /.well-known/autoconfig/mail/config-v1.1.xml /cgi-bin/autoconfig.cgi

ProxyPreserveHost On
ProxyPass / http://127.0.0.1:80/
RequestHeader set X-Forwarded-Port "443"
RequestHeader set X-Forwarded-Proto "https"

I used hitch to terminate ssl with below vcl config, it properly redirects to https - no issue. But with apache ssl termination, error of too many redirects loop.

VCL config:

sub vcl_recv {

if (std.port(server.ip) != 443) {
set req.http.location = "https://" + req.http.host + req.url;
return(synth(301));
}

    if (!req.http.X-Forwarded-Proto) {
        if(std.port(server.ip) == 443) {
            set req.http.X-Forwarded-Proto = "https";
        } else {
            set req.http.X-Forwarded-Proto = "https";
        }
    }

}
sub vcl_synth {

    if (resp.status == 301 || resp.status == 302) {
        set resp.http.location = req.http.location;
        return (deliver);
    }
}

Help me out in resolving my issue.

The issue you’re facing with the HTTP to HTTPS redirect loop in Apache when using Varnish is likely due to a misconfiguration in handling the X-Forwarded-Proto header, which causes the redirection to be misinterpreted by the server.

Here’s how you can fix the issue:

1. Configure Apache for Proper Redirect Handling

Update your Apache configuration to properly handle the X-Forwarded-Proto header. This will ensure that Apache understands when a request is already using HTTPS.

Modify your Apache configuration as follows:

apache

Copy code

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

    DocumentRoot /home/biolink/public_html

    ErrorLog /var/log/virtualmin/example.com_error_log
    CustomLog /var/log/virtualmin/example.com_access_log combined

    <Directory /home/biolink/public_html>
        Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch +ExecCGI
        AllowOverride All
        Require all granted
    </Directory>

    RewriteEngine On

    # Check if the X-Forwarded-Proto header is set to HTTPS
    RewriteCond %{HTTP:X-Forwarded-Proto} !https
    # Redirect to HTTPS if not
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:80/
    ProxyPassReverse / http://127.0.0.1:80/
</VirtualHost>

2. Ensure Varnish Passes the Correct Headers

Your Varnish configuration already sets the X-Forwarded-Proto header based on the port. This is good practice and should work as intended.

3. Disable Direct HTTP Access to Apache

Make sure that Apache is only accessible via the Varnish proxy (port 80) and the SSL termination (port 443). You can do this by ensuring that Apache is not listening directly on port 80 for public access.

4. Adjust Apache SSL Configuration

The SSL configuration for Apache should remain mostly the same, but make sure that Apache itself is not performing any additional redirections that might interfere with the setup.

apache

Copy code

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

    DocumentRoot /home/biolink/public_html

    SSLEngine on
    SSLCertificateFile /home/biolink/ssl.cert
    SSLCertificateKeyFile /home/biolink/ssl.key
    SSLCACertificateFile /home/biolink/ssl.ca

    ErrorLog /var/log/virtualmin/example.com_error_log
    CustomLog /var/log/virtualmin/example.com_access_log combined

    <Directory /home/biolink/public_html>
        Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch +ExecCGI
        AllowOverride All
        Require all granted
    </Directory>

    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:80/
    ProxyPassReverse / http://127.0.0.1:80/
    
    RequestHeader set X-Forwarded-Port "443"
    RequestHeader set X-Forwarded-Proto "https"
</VirtualHost>

5. Clear Cache

After making these changes, clear Varnish’s cache to ensure that any previously cached redirects are not causing issues:

bash

Copy code

varnishadm "ban req.url ~ ."

6. Restart Services

Finally, restart Apache and Varnish to apply all the changes:

bash

Copy code

systemctl restart apache2
systemctl restart varnish