commit 883734aa7e532aa6f46dfb986ed8b9790d62a2b6 Author: Reimar Date: Sat Aug 2 19:33:34 2025 +0200 Add upgrade insecure requests article diff --git a/static/assets/style.css b/static/assets/style.css new file mode 100644 index 0000000..1b29d91 --- /dev/null +++ b/static/assets/style.css @@ -0,0 +1,81 @@ +:root { + color-scheme: light dark; +} + +body { + max-width: 1000px; + margin: 0 auto; + padding: 0 20px; +} + +time { + color: #757575; +} + +.published { + color: #757575; + font-style: italic; +} + +.table-of-contents { + display: inline-block; + padding: 0 12px 8px 12px; + border: 1px solid black; + background-color: #F9F9F9; + line-height: 1.8; +} + +legend { + font-weight: bold; +} + +a { + color: #1565C0; +} + +h2 a { + color: inherit; + text-decoration: none; +} + +code { + background-color: #EEE; + border-radius: 4px; + padding: 0 4px; + white-space: pre; +} + +pre { + background-color: #EEE; + border-radius: 4px; + padding: 4px; + overflow-x: scroll; +} + +@media (prefers-color-scheme: dark) { + body { + background-color: #151515; + } + + time, .published { + color: #E0E0E0; + } + + .table-of-contents { + background-color: #242424; + border-color: #BDBDBD; + } + + legend { + color: #BDBDBD; + } + + a { + color: #42A5F5; + } + + code, pre { + background-color: #323232; + } +} + diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..01581b2 --- /dev/null +++ b/static/index.html @@ -0,0 +1,20 @@ + + + + + + Reimar's articles + + + +

Reimar's articles

+

This is where I publish my writings.

+
+ + + + diff --git a/static/upgrade-insecure-requests.html b/static/upgrade-insecure-requests.html new file mode 100644 index 0000000..1dbccec --- /dev/null +++ b/static/upgrade-insecure-requests.html @@ -0,0 +1,166 @@ + + + + + + + Handling the Upgrade-Insecure-Requests header in nginx + + + +

Handling the Upgrade-Insecure-Requests header in nginx

+ Published on +

+ Today it is considered a de facto security standard for the + web, that all HTTP requests automatically be redirected to + HTTPS to prevent Man-in-the-middle attacks. Of course, this + is a good standard to conform to, but there can also be + benefits with providing an insecure HTTP version of your + site as a fallback for users who, for one reason or another, + are not able to use HTTPS. You can read more about that + here. +

+

+ Luckily, the web specifications have a way for the client to + tell the server whether or not it wants insecure connections + to be upgraded or not: The + + Upgrade-Insecure-Requests + header. + By setting this header to 1, the client can + indicate to the server, that it wants any insecure HTTP + requests to automatically be upgraded to HTTPS. This has + also been implemented in all major browsers since 2018. This + article will show you how to handle this header using nginx. +

+ +
+ Table of contents + Configuring nginx
+ Using Cloudflare
+ Testing the configuration +
+ +

Configuring nginx

+

+ In its essence, the configuration is very simple: For every + request, we want to check if the scheme is http + and the Upgrade-Insecure-Requests header is set + to 1. If both of these conditions match, we + want to redirect to the same URL using HTTPS. +

+

+ Sadly, you cannot have if-statements in nginx with multiple + conditions, so we will have to do a workaround. What we can + do is combine the current scheme with the value of the + header into a string and match that against a specific + value. This can be done using map in nginx: +

+
+map "$scheme+$http_upgrade_insecure_requests" $upgrade {
+	default 0;
+	"http+1" 1;
+}
+

+ Here we combine the two values into a string with a + + in the middle, and compare it against the + string "http+1". If it matches, the + $upgrade variable is set to 1, + otherwise 0. +

+

+ Now we just need to redirect based on this variable. This + must be done inside the server block, while + the variable must be defined in the global scope. Make sure + also that the server is set to listen on both port 80 and + 443. +

+
+if ($upgrade) {
+	return 301 https://$host$request_uri;
+}
+

+ Putting it all together, we now have a functioning nginx + configuration which redirects properly based on this header: +

+
+map "$scheme+$http_upgrade_insecure_requests" $upgrade {
+	default 0;
+	"http+1" 1;
+}
+
+server {
+	listen 80;
+	listen 443 ssl;
+	
+	server_name example.com;
+	
+	location / {
+		# ...
+	}
+	
+	if ($upgrade) {
+		return 301 https://$host$request_uri;
+	}
+}
+ +

Using Cloudflare

+

+ If you are using Cloudflare, you will have to make some + changes to this configuration. In this case, all requests + your web server receives will be either HTTP or HTTPS + depending on your Cloudflare configuration, regardless of + the actual user's request. +

+

+ Luckily, Cloudflare sends a header along with its request, + telling whether the user wanted HTTP or HTTPS. This one is + called + + CF-Visitor, + and the value is actually a JSON string that could tell more + information about the request, but currently it only + contains a single key (the scheme). This makes it very easy + to handle, as we can just continue using string matching. +

+
+map "$http_cf_visitor+$http_upgrade_insecure_requests" $upgrade {
+	default 0;
+	"{\"scheme\":\"http\"}+1" 1;
+}
+

+ Changing the map-statement from before to the above should + make the configuration work with Cloudflare. Although this + solution is not completely future-proof, Cloudflare has kept + it like this for long enough, that I consider it pretty + safe. If they change it, HTTPS redirection will be + completely disabled. +

+ +

Testing the configuration

+

+ You can use curl to make sure the configuration works + properly. The --head option sends a + HEAD request and makes curl output the response + code and headers. +

+
+curl --head http://example.com
+

+ This should give a response code of 200 and not redirect. + Now you can add the header to the command: +

+
+curl --head http://example.com --header "Upgrade-Insecure-Requests: 1"
+

+ This should result in a 301 response with the + Location header set to the same URL but with + HTTPS. As a last check, you can test the HTTPS URL and make + sure it also returns 200. +

+ +

Back

+ +