Serving HTTP to old devices and HTTPS to new ones

Devices that pre-date modern encryption are still on the Internet. They shouldn’t be used for anything that needs security, but there’s still valid use-cases for serving content to them.

Newer devices, on the other hand, are increasingly hostile to unencrypted content. The popular notion is that HTTPS helps reduce attacks — and while true, and important for many scenarios, the encryption (and related certificate) really only validate that last link in a very long chain of trust that web clients have to accept.

Assuming you have good reason to support both scenarios, Joshua Stein’s excellent post explores how serve both HTTP and HTTPS in nginx. A snip here for my own records, but credit goes to him:

server {
    ...
    set $need_http_upgrade "$https$http_upgrade_insecure_requests";
    location / {
        if ($need_http_upgrade = "1") {
            add_header Vary Upgrade-Insecure-Requests;
            return 301 https://$host$request_uri;
        }
        ...
    }
}

To add to the conversation, I thought it was important to cover the other most common Linux web server, Apache2. So here’s how to conditionally serve HTTPS for clients that insist on it, while still serving HTTP to older clients in an Apache2 config using mod_rewrite:

<VirtualHost *:80>
    ...
    <Location>
        <If "%{req:Upgrade-Insecure-Requests} == '1'">
            RewriteEngine On 
            RewriteCond %{HTTPS} !=on 
            RewriteRule ^/?(.*) https://%{SERVER_NAME}%{REQUEST_URI} [R,L]
        </If>
    </Location>
</VirtualHost>

Leave a Reply

Your email address will not be published. Required fields are marked *