Apache virtual hosts with multiple domains

Apache2dnsredirectsslvirtualhost

I am trying to figure out what the optimal configuration is for my virtual host files, but I'm having some issues with it.

Currently I have 2 domains: domain1.com and domain2.com

I have 1 server at IP 000.000.000.001 which actually hosts all the needed files. In these files you have an API (api.domain1.com) and the actual website.

domain1.com is using the nameservers of Digital Ocean and uses the following DNS records:

A        @        000.000.000.001
CNAME    *        domain1.com.

For SEO purposes I want to redirect all requests which are not being made to the api subdomain to forward to www.domain1.com.
However, I also ONLY want users to be able to browse my site (and API) through a SSL connection, I don't want users being able to use it through HTTP so I try to redirect all those requests to use HTTPS. The certificates are provided by LetsEncrypt!. Their automated install made changes to my virtual hosts files and currently I have 4 of them:

  1. api.domain1.com.conf

    <VirtualHost *:80>
        ServerName api.domain1.com
    
        DocumentRoot /var/www/api.domain1.com/web
    
        RewriteEngine on
        RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [L,QSA,R=permanent]
    </VirtualHost>
    
  2. api.domain1.com-le-ssl.conf

    <IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerName api.domain1.com
    
        DocumentRoot /var/www/api.domain1.com/web
    
        ExpiresActive On
        /* ... */
    
        <Directory /var/www/api.domain1.com/web>
            AllowOverride None
            Order Allow,Deny
            Allow from All
    
            <IfModule mod_rewrite.c>
                    Options -MultiViews
                    RewriteEngine On
                    RewriteCond %{REQUEST_FILENAME} !-f
                    RewriteRule ^(.*)$ app.php [QSA,L]
            </IfModule>
        </Directory>
    
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    
        SSLCertificateFile /etc/letsencrypt/live/domain1.com/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/domain1.com/privkey.pem
        Include /etc/letsencrypt/options-ssl-apache.conf
        SSLCertificateChainFile /etc/letsencrypt/live/domain1.com/chain.pem
    </VirtualHost>
    </IfModule>
    
  3. domain1.com.conf

    <VirtualHost *:80>
        ServerName domain1.com
    
        Redirect 301 / https://www.domain1.com
    </VirtualHost>
    
  4. domain1.com-le-ssl.conf

    <IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerName www.domain1.com
        DocumentRoot /var/www/domain1.com
    
        ExpiresActive On
        /* ... */
    
        RewriteEngine On
        RewriteCond %{HTTP_HOST} ^https://domain1.com
        RewriteRule ^/(.*)$ https://www.domain1.com/$1 [L,R=301]
    
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    
        ErrorDocument 404 /404.html
    
        SSLCertificateFile /etc/letsencrypt/live/domain1.com/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/domain1.com/privkey.pem
        Include /etc/letsencrypt/options-ssl-apache.conf
        SSLCertificateChainFile /etc/letsencrypt/live/domain1.com/chain.pem
    </VirtualHost>
    </IfModule>
    

When I try to visit the site, the following things happen:

    http://test.domain1.com   -->    https://test.domain1.com   FAIL
    http://domain1.com        -->    https://www.domain1.com    SUCCESS
    http://www.domain1.com    -->    https://www.domain1.com    SUCCESS
    https://domain1.com       -->    https://domain1.com        FAIL
    https://www.domain1.com   -->    https://www.domain1.com    SUCCESS

As you can see, the first and fourth entry fails. They both return a 404 Not Found to my visitors which is unacceptable to me. The first one even shows a SSL warning (I don't think that LetsEncrypt! supports wildcards so I think this warning cannot be prevented?)

What is causing my test. not redirecting to www.?

What is causing my fourth entry to end up serving a 404?

Now I also want my second domain (domain2.com) to point to this server/files. This domain is hosted at a different location (I got it from a friend) and is using different nameservers. I cannot simply change the nameservers (I think?) because this second domain has email hosting linked to it which uses the nameservers of this other provider. Momentarily, this domain also has its own web server (but this will go away in the future) hosted at 000.000.000.002. Right now, it uses a

 <meta http-equiv="refresh" content="0; url=https://www.domain1.com" />

tag in its index.html file to redirect to the right server, in the future this will have to be done in the DNS records though.

How should I do this?

So to summarise:

  1. I thought the CNAME * is a catch-all for all subdomains?
  2. What did I do wrong in the virtual hosts file? Something is causing that fourth redirect to fail.
  3. How should I handle my second domain pointing at my first IP?
  4. Is there another way to only allow HTTPS connections? (This should also be forced on the API virtual host). I've heard of HSTS, should I implement this?

Some SEO tests also pointed out that I need a IP redirect to further improve my SEO results. They give this as an example:

    RewriteCond %{HTTP_HOST} ^000\.000\.000\.001
    RewriteRule (.*) http://www.domain1.com/$1 [R=301,L]

Should I also implement this for SEO purposes? Should I also implement this for my other IP address?

So many questions…

Of course, if you have any other suggestions, please share your opinion!

Best Answer

Lots of questions there! Let's try to work through them:

  1. Not sure what you are asking here, and not an expert in CNAMES but would have thought you'd need '*.domain1.com' at least instead of just '*'

  2. For both you fails you do not explicitly note your alternative server names in either ServerName nor ServerAlias. In this case it will default to the first server found in the config. Is this the api.domain.com one? If so that might explain the 404 as the SERVER_NAME will (by default) be the client supplied one so the api.domain1.com:80 settings will just redirect to the https version of what they sent (which is what appears to be happening), which will then also look at the api.domain1.com:443 config and not find the name. You would need to either add the alternative names to ServerAlias setting of your main domain1.com config, or move that config to be first so it becomes the default and the api one is only used when the api.domain1.com name is explicitly used.

  3. As you appear to want to redirect this straight to your new site I would update the DNS to point to your new IP address, to save you paying for two servers (in terms of money and time to administer). This can be done on the existing DNS nameserver to keep the e-mail though obviously would be nicer to consolidate on one. In the meantime implement a proper 301 redirect in the server config as the meta redirect is not as strong a signal to search engines and you do seem to care about SEO.

  4. The main way is to redirect http to https. HSTS is primarily a security feature which automatically redirects http to https before the browser makes the request. This is useful as the default is http (e.g. typing www.example.com in the browser address bar will, by default, send you to http://www.example.com rather than https://www.example.com), and this can be intercepted to prevent an upgrade to https (which is more difficult to then intercept). It has some downsides though: Not all browsers support it, you need to visit the website at least once to load the HSTS setting (though some browsers allow pre-loading of this), and the setting will expire if the site is not visited in the maxAge time (again assuming it's not preloaded). So it should not be used as a replacement to redirects but more as an enforcement of it for browsers that support HSTS. Blogged about it here if you want to read more on HSTS.

I would also say you have several different methods of redirecting (api:80 has Rewrite, domain1:80 has Redirect, domain1:443 has Rewrite - but with a very restrictive and incorrect RewriteCond). Personally I prefer to use one method as it's less confusing but that's a personal matter (Redirect is also slightly faster but Rewrite gives you more power so I prefer that one). So I tend to have config like this:

<VirtualHost *:80>
    #This is the first host so is the default.
    #So although I've specified a ServerName and ServerAlias anything else not specified elsewhere will also end up here.
    ServerName www.domain1.com
    ServerAlias domain1.com

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    #Redirect everything to https:
    RewriteEngine on
    RewriteRule ^(.*)$ https://www.domain1.com$1 [R=301,L]
</VirtualHost>
<VirtualHost *:80>
    ServerName api.domain1.com

    ErrorLog ${APACHE_LOG_DIR}/api.error.log
    CustomLog ${APACHE_LOG_DIR}/api.access.log combined

    #Redirect everything to https:
    RewriteEngine on
    RewriteRule ^(.*)$ https://api.domain1.com$1 [R=301,L]
</VirtualHost>

and then for the 443 I would have this:

<VirtualHost *:443>
    #This is the first host so is the default.
    #So although I've specified a ServerName and ServerAlias anything else not specified elsewhere will also end up here.
    ServerName www.domain1.com
    ServerAlias domain1.com

    ErrorLog ${APACHE_LOG_DIR}/ssl.error.log
    CustomLog ${APACHE_LOG_DIR}/ssl.access.log combined

    #Redirect everything which is not already on the real www domain name to that:
    RewriteEngine on
    RewriteCond %{HTTP_HOST} !www.domain1.com
    RewriteRule ^(.*)$ https://www.domain1.com$1 [R=301,L]

    #all your other 443 config for www.domain1.com

</VirtualHost>
<VirtualHost *:443>
    ServerName api.domain1.com

    ErrorLog ${APACHE_LOG_DIR}/ssl.api.error.log
    CustomLog ${APACHE_LOG_DIR}/ssl.api.access.log combined

    #all your other 443 config for api.domain1.com

</VirtualHost>

Note that I've added different log files for each vhost so it's more obvious which vhost is being hit.

Note also that I redirect anything which is not www.domain1.com rather than your version which only redirected if people typed domain1.com (and which I'm not sure would be used as suspect api.domain1.com config came first).

This will also take care of anyone accessing your website by IP address, or any other name as it will always be redirect to your real www.domain1.com server name. This is easy for http requests, but for https requests they may indeed get a cert error if your cert does not include the servername used.

LetsEncrypt do not support wildcards, but they do allow multiple names on a cert so you can easily get a cert for domain1.com and www.domain1.com and you should as this is generally considered best practice. You could also add any other domains you think might be used. For you api.domain1.com domain you could also add to this same cert, or get a separate cert for that since you have separate config for that (your config above is not clear on which you are doing for that since it looks like same config but not sure if that's a typo or not).

For SEO purposes it is indeed best to only serve your site under one URL and use redirects to enforce that URL and above config will do this. This is to prevent Google thinking you have duplicate content if you server the same content under www.domain1.com/page1.html and https://www.domain1.com/page1.html and https://domain1.com/page1.html and http://000.000.000.001/page1.html ...etc.

A lot covered there but hopefully points you in the right direction.

Related Topic