Http-https transitions and relative URLs

Wednesday 17 October 2007This is 17 years old. Be careful.

When building a web site with HTTPS pages, one of the annoying tasks is to ensure that those pages make no references to HTTP resources. If they do, then Internet Explorer will pop up alarming messages about mixing secure and insecure content, and do you want to display the insecure content. This confuses users and generally discourages them from continuing to use your site.

To fix the problem, all URLs on the page must use HTTPS. Web sites these days are built from reusable components, some of which may be used on both HTTP and HTTPS pages. How to make the URLs correct for both?

Relative URLs are the answer. Typically, relative URLs omit the hostname, specifying the full path to the resource, or perhaps just a single path component:

<img src='/pix/smiley.jpg' />
<img src='smiley.jpg' />

Either of these images will display without warning on either HTTP or HTTPS pages. Since the host name is omitted, it uses the host name from the page being displayed. But these URLs also omit the protocol scheme, so the protocol is taken from the base URL also. On an HTTP page, the images will be requested using HTTP, on an HTPPS page, they will use HTTPS. That’s why there’s no warning, because there’s no mixing of secure and insecure content.

But what if you need to pull resources from another site? For example, a CDN (content delivery network)? Here the problem seems to be thornier:

<img src='http://fast.cdn.net/pix/smiley.jpg' />

If this reference appears in an HTTPS page, the mixed content warning will appear. How to craft a reference that works for both? The answer is again relative URLs, but using a more obscure syntax:

<img src='//fast.cdn.net/pix/smiley.jpg' />

Here, we’ve left off the protocol scheme, but included a host name. In this case, the protocol scheme from the displayed page will be used, but against the host in the URL. The relative URL system is still in play here: omitted portions of the URL at the beginning are taken from the base page, and the relative URL takes over wherever it starts. On an HTTPS page, this will be an HTTPS request to the CDN, on an HTTP page, it will be an HTTP request.

You have to be careful to only use this syntax in pages destined for browsers. If you put it in an email, there will be no base page URL to use in resolving the relative URL. In Outlook at least, this URL will be interpreted as a Windows network file, not what you intended.

BTW: when trying to get rid of the mixed secure and insecure content warning, you really have to fix up every URL, even if it doesn’t seem like it’s being used for anything. Flash content? There’s a macromedia.com URL in there in the codebase attribute:

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
   codebase="//download.macromedia.com/pub/shockwave/cabs/flash/blah#blah"
   width="600" height="400"> ... </object>

Making it scheme-relative as shown will also prevent warnings.

Comments

[gravatar]
Wow, you do learn something every day. I didn't realize that the full url minus the protocol was valid syntax. Interesting.
[gravatar]
Thanks! That's damn nice.

BTW, do you know in which HTML version did this appear?
[gravatar]
Amazing. I have never seen that syntax before.

Also, Google Analytics provides a secure reference to their .js file as well.
[gravatar]
Great ! Thanks very much.
[gravatar]
Very clever. Thanks. Also, your previous two posts are fantastic. Here's one more subscriber to your RSS feed.
[gravatar]
Which browsers does this work in?
[gravatar]
I had no idea about this, though it doesn't work for me...
Would love to see it working. Do you have it running somewhere?
[gravatar]
I don't know in which version of HTML it appeared. The HTML 2 spec references RFC 1808 which describes this behavior, and was written in 1995.

I know this syntax works in IE6, IE7, FF2, and Safari 2 and 3. I don't know of any browsers in which it doesn't work.

I think this is not a case of new syntax, but of a specialized and little-used syntax.
[gravatar]
dave g: URLs like this are on tabblo.com now. Load the home page and look at the source....
[gravatar]
The big downside with relative urls in blog posts is that they never get translated in the outgoing RSS feed.

complete pain in the butt.
[gravatar]
Wow - I never realised that. Another snippet for the HTML box of tricks!
[gravatar]
clever!
I wish most of the web developers who use https should read this post.
@engtech : The contents of the secure pages are hardly pushed into the RSS feeds anyway. So I don't think that's a big downside.
[gravatar]
Very interesting. Could also be used to shirk page file sizes by a few bytes, even on normal HTTP sites.
[gravatar]
Oops, shrink, not shirk.
[gravatar]
I had no luck with this technique - IIS server refused to serve up the image when referenced this way - main environment was HTTPS, image was on HTTP server.
Does this assume a certain default server or browser setting?
[gravatar]
The server settings have nothing to do with it: the resolution of a relative URL into a server request is all done at the browser.

If you install Fiddler, you'll be able to see the requests the browser is making. That should help find the problem.
[gravatar]
Ok, I've been building websites since 1996, and I had absolutely no idea about this. I guess you do learn something new every day. :-)
[gravatar]
Awesome. I swear I had Saved As New this post in my Bloglines account... but which RSS feed was it? To paraphrase Michael Corleone, "Ned, I knew it was you." Found it, used it, great tip.
[gravatar]
Ned, that's a great tip. I'm going to try using it right now. Thanks for publishing it in a concise, clear form.
[gravatar]
So, this represents your (the user's) agreement to also trust the linked third party? How do browsers display this additional trust / certificate?
[gravatar]
Just to clarify, even if the certificate is authenticated, it is STILL bringing the third party into the communication.
[gravatar]
Fantastic find! Thanks for the tip.
[gravatar]
It's 2009 people. All of the content you put in front of the Customer should be done in something like ExtJS.com, GWT or a finite number of other JavaScript-targeting frameworks that have dedicated, if not massive, teams behind them. That means the only "httpS" you'll ever need to worry about is in the base-URL for your back-end server's AJAX responder.
[gravatar]
Pas B,

The point of the "mixed secure and insecure content" warning is that the browser should not show the padlock icon - symbolic of "this content was encrypted" - on a page where some of the content on the page was not encrypted.

Browsers will not usually expose the certificate information of the certificates on 3rd party sites loaded into the page unless they are invalid.

The "trust" implied here only goes as deep as validation of identity - it's assumed that if every certificate is valid and signed by a root authority, then you can trust the content to be authentic.
[gravatar]
Actually, the point is to automatically manage the page so that if the HTML is served over https, then the rest of the content is also, and if the HTML is not served over https, then none of the content is.

The browser warnings happen when you mix the protocols, and this post shows a simple solution for avoiding the mixing.
[gravatar]
What about links, when i am using https://www,doamin.com/securepage.asp to browse page then all non secure links appears secure meaning if I want to go back to my non-secure page from a secure page then URL shows me https
[gravatar]
If you have a page served by https, and you want the links from it to use http, then you have to provide the http: scheme in the URL. Leaving it out will default the scheme to https.
[gravatar]
This depends on the server that you're linking to understanding https. Not all servers do. This may be why some of the people above had trouble getting it to work. If you have on a page, it will look fine in http, but the image won't show up in https.
[gravatar]
Sine Language,
Not everything is a web application. Some of us develop sites that we like Google to be able to index, so grabbing all of the context via ajax is not a viable solution.

Klortho,
That's a good point. You want to make sure the server supports https before using this technique.
[gravatar]
Will these urls be indexed properly by Google?
[gravatar]
Great solution. It worked for me.

-Thanks
[gravatar]
Leaving it out will default the scheme to https.
[gravatar]
@drpciv: no, leaving out the scheme will default it to the scheme the current page is served with. On an http page, it defaults to http. On an https page, it defaults to https.
[gravatar]
Thanks for writing this article just got me out of tight spot!

I first saw this technique used in the markup of the html5 boilerplate to load jQuery in either http or https environments, this of course came years after this post was written.
[gravatar]
Just out of interest does this have any performance issues?
[gravatar]
@Stefan, I haven't measured it, but there's no reason to think there's any performance impact from this. This works for the same reason that the page "http://example.com/a.html" with a link to "b.html" will find b.html on example.com. It's simple string manipulation as part of making a relative URL absolute.
[gravatar]
I have discovered this doesn't work with CSS files in IE7 possibly IE8 as the browser will download the images twice which is a performance hit.

http://www.stevesouders.com/blog/2010/02/10/5a-missing-schema-double-download/
[gravatar]
Thanks for the article, Ned. Learnt something new
[gravatar]
very nice article. solved my problem. I was using a company header image in html email body where our QA environments run on http and production environment run on https. So I was confused how I am gonna add the image url. So i just skipped the protocol part and used the relative url as //abc.pqr.in/_layouts/images/header.jpg.
And its working fine.
[gravatar]
My sites are all wordpress so is there a certain file you need to do this in or is there a plugin that will do this job?
[gravatar]
I have tried several ways in which I found on the internet, but nothing worked

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.