In this post, I explain how I fixed the “mixed content” security issue when using Cloudflare Flexible SSL, and IIS Rewrite.
I Run Two Websites Under One Account Using IIS Rewrites
I have two websites that are hosted under one account with my hosting provider (I know!): https://emadashi.com and https://dotnetarabi.com. The way I do it is that is by using IIS Rewrite rules in my web.config; any request that is targeting one of these domains, I “rewrite” the URL so it is pointing to the sub-directory to serve the request. This changes where the file is served from, but does not change the request URL to the user.
However, if by any chance a request came to the server targeting the sub-directory itself, that page will still be served as is, which is not desirable as I don’t want to expose the inner of my websites; it’s ugly and bad for my websites’ URL discovery. In this case, first I want to “redirect” the user to point to the domain without the sub-directory; and then run the rewrite rule as mentioned above, which I did.
In psudo, when a request comes the execution of the rules looks like this:
- Rule1: Does the URL include a sub-directory? If so then Redirect to the same URL without the sub-directory.
- Rule2: The URL does not include the sub-directory, so Rewrite (not Redirect) to the sub-directory.
I want to Serve My Websites Over HTTPS, But…
Now when I wanted to secure my websites and start using HTTPS to serve requests, thanks to Troy Hunt’s continuous nagging :P, I couldn’t just use normal certs with my hosting due to the way I am running it. So again, based on Troy Hunt’s awareness efforts, I used Cloudflare’s Flexible SSL free service.
This went fine until I discovered that engine of dotnetarabi generated guests images’s URLs including the sub-directory. When I open dotnetarabi over HTTP, the first request to these URLs is HTTPS, but of course containing the sub-directory, the second request though (which is a redirect to the URL without the sub-directory) is always coming back as HTTP! This caused the known “unsecure; mixed content” problem.
Simply, the reason is that:
- With Flexible SSL, Cloudflare communicates to your server view HTTP ALWAYS; you don’t have certs, this is why you need them in the first place!
- Cloudflare Flexible SSL doesn’t force HTTPS if you haven’t explicitly asked it to (via the Always Use HTTPS option). So if the request came view HTTP, it will pass it through as HTTP.
So in the the case of my redirects above, what happens is the following:
- The request comes to Cloudflare via HTTPS, the URL include the sub-directory
- The request is forwarded to my server via HTTP (NOT HTTPS!) to the sub-directory
- My server innocently redirects the request to the URL without the sub-directory, but using the same protocol the current request is using, which is HTTP because it will always be!
- The user receives the redirection to the new URL, but with the HTTP protocol this time, and then Cloudflare just passes it through because it does not force HTTPS.
The solution
The trick was that it’s true that Cloudflare does not use HTTPS when it forwards the request to your server, but what it does is that it adds the header X-FORWARDED-PROTO=https to the requests to your server if the original request was using HTTPS.
So, all what I needed to do is to check on this header in my redirects; if it exists then redirect to HTTPS, otherwise redirect to HTTP:
The Action part of my rule:
<action type="Redirect" url="{MapSSL:{HTTP_X_FORWARDED_PROTO}}dotnetarabi.com/{C:1}" appendQueryString="true" logRewrittenUrl="false" />
<rewriteMaps> <rewriteMap name="MapSSL" defaultValue="https://"> <add key="https" value="https://" /> <add key="http" value="http://" /> </rewriteMap> </rewriteMaps>