I just recently took CSS-Tricks “HTTPS everywhere”. That is, every URL on this site enforces the HTTPS (SSL) protocol. Non-secure HTTP requests get redirected to HTTPS. Here are some notes on that journey.
Why do it?
- General security. When you enforce HTTPS, you’re guaranteeing no information passed between the server and client can be intercepted and stolen or messed with in any way. That’s great for a site like this that has a login system and accepts credit cards in some places and things like that.
- The site as it is intended. I’ve heard of examples like hotel WiFi system and even ISPs that mess with HTTP traffic and do things like insert their own advertising code. Can’t do that over HTTPS.
- SEO. Google says you’ll rank higher. Horse’s mouth.
- Prereq. I can’t seem to find any good links on this, but I’m under the assumption that HTTPS is required for some/all of the stuff for SPDY / HTTP/2 – which everyone agrees is awesome and super fast. I want to make sure I’m ready so I can start moving forward on that.
- Geek cred. Duh.
1. Get an SSL certificate
Not optional. This is how it works. I’ve done this a bunch of times in my life and it’s never overly comfortable. You just need to follow instructions closely. I’ve bought them in the past from discount providers and actually had it work fine, but it’s a more manual process.
CSS-Tricks is happily on Media Temple, and they provide SSL as a service, and I’m more than happy to pay for that to have it installed painlessly by professionals.
When the SSL certificate is installed properly, you should be able to visit your site (any URL) at either HTTP or HTTPS and have it come up fine. There may be errors on HTTPS though, and we’ll get to that.
2. Start with the Admin
In WordPress-land, you might as well get HTTPS going in the admin area first. It’s set up to handle it and there probably won’t be any errors. (I keep saying “errors”, I mostly mean “mixed content warnings” which I promise we’ll get to.)
To force HTTPS in the admin area, put this line in your wp-config.php file at the root of your WordPress install:
define('FORCE_SSL_ADMIN', true);
Make sure you test that HTTPS is working properly first! Go to https://yoursite.com/wp-admin/ to check. Otherwise you’ll be forcing URLs that don’t work and that’s bad. If you have trouble, remove that line right away.
All goes well, you’ll get a secure connection:
3. Try to get one page working on the front end
The next step is to get your front end on HTTPS. Forcing it all right away is probably going to be tough, so just start with one target page. For me, it was the signup page for The Lodge. That page can take credit cards, so really, it had to be HTTPS. This was the motivator for me early on to get this set up.
There is a plugin that can help with this: WordPress HTTPS (SSL). With that plugin, you get a checkbox on Posts/Pages to force it to be SSL.
4. Mop up Mixed Content Warnings
What you’re really trying to avoid is this:
That’s like: “Hey nice trying being HTTPS but you aren’t fully so NO GREEN LOCK FOR YOU!”
If you open the console, you’ll likely get messaging like this:
In this case, it was some images being used in a CodePen embed with an HTTP src.
But it could be anything. HTTP <script>
s, HTTP CSS <link>
s, HTTP <iframe>
s. Anything that ends up making an HTTP request that isn’t HTTPS will trigger the error.
You just need to fix them. All.
5. Protocol Relative URLs! (or just relative URLs)
You know, those ones that start with //, like this:
<img src="//example.com/image.jpg" alt="image">
Those are your friend. They will load that resource with whatever protocol the current page is. And links that are just relative to begin with will be fine, like:
<img src="/images/image.jpg" alt="image">
I ended up finding them all over the place. I even had Custom Fields to fix:
6. Harder problem: images within legacy content
There are thousands and thousands of pages on this site, and lots and lots of images within those pages. Right in the content itself. There are a bunch on this very page you’re looking at. The problem is those images had fully qualified HTTP links on them.
I didn’t relish the idea of using some WordPress filter on content to kinda hotswap those URL’s out – I just wanted to fix the issue. I hired Jason Witt to help me with this. The first thing we did was run some SQL on the database to fix the URL’s. Essentially fixing the src
of images to be protocol relative.
After backing up the database, and testing it locally, this worked great:
UPDATE wp_posts
SET post_content = ( Replace (post_content, 'src="http://', 'src="//') )
WHERE Instr(post_content, 'jpeg') > 0
OR Instr(post_content, 'jpg') > 0
OR Instr(post_content, 'gif') > 0
OR Instr(post_content, 'png') > 0;
And another to catch single-quoted ones, just in case:
UPDATE wp_posts
SET post_content = ( Replace (post_content, "src='http://", "src='//") )
WHERE Instr(post_content, 'jpeg') > 0
OR Instr(post_content, 'jpg') > 0
OR Instr(post_content, 'gif') > 0
OR Instr(post_content, 'png') > 0;
We even fixed the Custom Fields in much the same way:
UPDATE wp_postmeta
SET meta_value=(REPLACE (meta_value, 'iframe src="http://','iframe src="//'));
7. Make sure new images are protocol relative
With the legacy content mopped up, we need to make sure new content stays OK. That means fixing the media uploader/inserter thingy so it inserts images with protocol relative URLs. Fortunately I already customize that to insert with <figure>
tags, so it was just an adjustment of that. Jason ultimately helped me move a lot of my custom functions.php code into a plugin, so this is a little out of context, but I think you’ll get the picture and see the relevant filters and stuff:
class CTF_Insert_Figure {
/**
* Initialize the class
*/
public function __construct() {
add_filter( 'image_send_to_editor', array( $this, 'insert_figure' ), 10, 9 );
}
/**
* Insert the figure tag to attched images in posts
*
* @since 1.0.0
* @access public
* @return string return custom output for inserted images in posts
*/
public function insert_figure($html, $id, $caption, $title, $align, $url) {
// remove protocol
$url = str_replace(array('http://','https://'), '//', $url);
$html5 = "<figure id='post-$id' class='align-$align media-$id'>";
$html5 .= "<img src='$url' alt='$title' />";
if ($caption) {
$html5 .= "<figcaption>$caption</figcaption>";
}
$html5 .= "</figure>";
return $html5;
}
}
8. Your CDN needs SSL too
If you have a CDN set up (I use MaxCDN through W3 Total Cache) that means the CDN is totally different server and URL and all that and it needs to be able to serve over HTTPS as well. Fortunately MaxCDN handles it.
9. Start forcing HTTPS everywhere
This is what I do in the .htaccess file at the root:
# Force HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Once that is in place, you can turn off the plugin and remove the bit from the wp-config.php file, as those are redundant now.
You’ll also want to change the URL settings in General Settings to be https:// – so all the links in WordPress are constructed properly and redirects don’t need to come into play:
10. Keep mopping up
Inevitably after going HTTPS everywhere, you’ll find pages with mixed content warnings. You just need to keep investigating and fixing.
Good luck! (and let me know if I’ve fouled anything up)
Update! Speaking of mopping up… Content Security Policy (CSP) is a technology that might be incredibly helpful for you here. There is a directive for it, upgrade-insecure-requests, that can force the browser to use HTTPS for any HTTP resources it finds. A bit of a sledgehammer, but could be super useful.
If you’re transitioning to HTTPS now and you’re getting a grey/yellow lock although you have no mixed content, it could be a problem with your SSL certificate. Modern browsers are currently phasing out support for certificates using “SHA1” as a hashing algorithm (technical term) for security reasons (Too the point where, sometime in the future, a SHA1 certificate will display a red “insecure” warning), so you’ll want to check with your SSL provider that your new certificate (including the whole “certificate chain”) is using SHA2 or newer.
Just something I wanted to get off my chest, since I ran into problems with this before.
This is a great check list. I’ve also done this a number of times and agree with your cautious words on it (being a bit of a pain to set up right). My only comment is regarding Media Temple issued certificates. They are not issued from your own domain and will throw up warnings to the end user. This warning basically asks if the user wants to trust your site. It makes your sites look like spam. I had to remove the certificate because of this, but despite many, many communications with MT, some sites were still getting random certificate warnings several years on – to the huge detriment of site traffic. So, the only point I have to disagree on, is using the Media Temple supplied certificate (I think they come from GoDaddy). Officially, MT provide no support when things go catastrophically wrong, and there is actually nothing in the control panel or Account Centre that can fix it – it has to be done in server command line.
I have used this plugin in the past to help me update the links
https://wordpress.org/plugins/velvet-blues-update-urls/
Plugin is a good solution, but I recommend you to do all manually like in this article. Or, you can try this options too http://designmodo.com/wordpress-https/.
The plugins can hard load your website.
You should talk to your hoster about their SSL-setup:
https://www.ssllabs.com/ssltest/analyze.html?d=css-tricks.com&s=199.83.132.152&hideResults=on
Chris, see https://freakattack.com/ for more information on the issue. It looks like Media Temple is using the same unsafe configuration for their own https://mediatemple.net, so at least they’re consistent. Haha.
Never mind, my bad. FREAK is RSA_EXPORT, while SSL Labs is dinging your score due to RC4. Different weak standard. :)
Interesting writeup for a manual cleanup. I ended up doing a search and replace explicitly for strings with my own domain, using WP-CLI.
I’d recommend for others to do so table by table, or at least exclude the guids, else you’ll re-trigger RSS and it’s going to be a bad time if you have any auto-posting scripts installed.
I accidentally said table by table, but the
guid
is a column inwp_posts
, to clarify :)A quick clarification: SPDY does indeed require SSL. HTTP2 does not. However, some implementations may require it.
This post couldn’t have come at a better time. Preparing to push a few WP sites to HTTPS soon. Thanks for the awesome write up, Chris!
For those using WP-CLI, you can migrate an existing blog (database) to HTTPS with the following command:
wp search-replace ‘http://domain.com’ ‘https://domain.com’
(This is a good time to make sure you don’t have lingering http://www.domain.com references in the code too, by running a second search-replace for http://www.domain.com -> https://domain.com)
This will set WordPress straight, but you should still add rules to your server configuration to catch traffic not handled by WordPress (like a direct hit to a static asset).
Be careful when using protocol-relative URLs for external assets (e.g. hot-linking images, or using CDNs for javascript/css libraries), because if the remote domain does not have an SSL cert/support HTTPS the request will fail.
This will usually be more of an issue for images than CDN-hosted assets, as any CDN worth its salt should support HTTPS.
Bear in mind Google have said: “We’re starting to use HTTPS as a ranking signal. For now it’s only a very lightweight signal — affecting fewer than 1% of global queries.”
Perhaps I’m the only one to worry about:
1) Exhausting the limited number of remaining IP addresses (OK I admit IPv6 will fix this).
2) The increase in the amount of processing a webserver will be required to do to serve ALL sites over HTTPS.
3) Webmasters forgetting to renew SSL certs on time and destroying their traffic due to “this site is unsafe” warnings in visitors’ browsers. OK this shouldn’t happen on a busy ecommerce site but I guarantee it will happen on some smaller WordPress sites that don’t really need to serve content over SSL.
1) Thanks to TLS-SNI HTTPS servers don’t need dedicated IP addresses anymore.
2) That increase is negligible, due to hardware implementations of crypto algorithms in modern CPUs.
3) Valid point, although it’s not a new problem. If you forget to regularly patch your servers, a hacker will take down your site too. Also, a major benefit of using encryption everywhere is that it increases “background noise”, making encrypted transmissions less suspicious.
1) Most modern browsers now support SNI so a separate IP address won’t be a necessity either. The holdout browsers mostly include older versions of Android, IE on Windows XP, and older versions of Safari on Mac OS X 10.5).
2) Depending on the site, there’s not really any additional overhead.
It always amazes me when google says jump, people do it, regardless of whatever it is. If someone is having problems ranking, don’t go straight to https or the latest google signal—start by writing better content and driving traffic to it first. IMO unless you run an e-commerce site, it’s not really worth the effort for a small blog to go fully HTTPS (yet) but this article does a good of detailing the steps involved.
Good writeup. One thing I think should be mentioned (file it under A+) is HSTS which basically prevents the visitors browser from ever making more than one request on port 80. It’s sort of like saying “Next time you want to come here, go straight to port 443”.
.. which is somehow different from what happens with HTTP 301, but I’d be lying if I said I knew how without asking Google. :-)
As you mention, HTTP/2 & SPDY use TLS. The RFC doesn’t mandate using TLS but, lot of internet components like cache servers and proxies may not understand HTTP/2 immediately (SPDY will be discontinued). To mitigate against such issues, it is simpler to tunnel the requests over TLS and immediately gain advantage of the newer protocol.
I just wanted to say that this is a great writeup, very comprehensive and definitely something I’ll keep in mind for a move to SSL, which I’ve been doing a lot lately esp for WP. That in mind, I’m surprised of your switch. I saw a lot of sites switch after Google’s announcement a few months ago, now a lot of folks are switching back. It doesn’t seem like a bad decision, but I think the pros are a little exaggerated in this case. Most site owners who switched haven’t seen a rankings increase, but it is still early so we shall see.
Again, very comprehensive writeup quite possibly one of the best I’ve seen yet.
Also: don’t forget if you are using Google Webmaster Tools you are going to have to go in there and do a change of address (or re-add your site) this feature still might be broken in WMT as well as sitemap.xml etc
Your robots.txt also links to the non-http version of sitemap_index.xml which might be causing an issue because when I try to load it in the browser I get all white.
I’m agree with you. HTTPS on the hand is a URI scheme which has identical syntax to the standard HTTP scheme, aside from its scheme token. However, HTTPS signals the browser to use an added encryption layer of SSL to protect the traffic.
In a nutshell, SSL is the standard that defines how connections are encrypted via HTTPS.
Why webmaster should using HTTPS? Because HTTPS is especially important over unencrypted networks (such as Wi-Fi), as anyone on the same local network can “packet sniff” and discover sensitive information.
How many times have you accessed a site on an open network and got unexpected ads?
When you serve your website content securely via HTTPS, you are guarantee that nobody will alter how they are received by users. If you’re serious about doing business online, you need SSL. It’s the best way to protect user data and defend against identity theft.
Many customers will refuse to do business with a website that doesn’t have an SSL certificate. Displaying your SSL Site Seal tells customers they can shop or use a website with confidence, knowing they are protected.
You might also want to make sure that your session cookies are flagged as secure only. You do that in your php.ini file.
session.cookie_secure=1
. See: https://www.simonholywell.com/post/2013/05/improve-php-session-cookie-security.html for more info.Thank you for sharing, this was a great read. I agree with you, I would rather have someone else install the SSL certificate because its a painful process and hate doing it.
I’m attracted to this idea too. One thing that wasn’t mentioned (and it’s something I struggle with every time I consider defaulting all our sites to HTTPS) is whether to use
gzip
on your assets. Apparently using compression over a secure connection can open you up to the CRIME vulnerability. This kinda sucks, because we all agree compression is good. Except when it’s not, like when someone can steal your session cookie.I have just made this guide on my website Esfera Textual. I had the problem 4 in some website pages, but by editing the code it worked perfectly.
Thank you very much for this post and this valuable content.
Really love this guide. I have just added SSL encriptation on Rincón de la Tecnología. Now I’m going to add it to Rincón del Músculo.
Thank you very much Chris!!
Before I get all negative, I do want to thank Chris for a well-researched article. I definitely plan to move my own personal site to HTTPS ASAP.
But…Is anyone thinking about user experience here? Sorry not to join the chorus of undiluted approval, but how can I ignore the horrific, scary user experience that awaits any visitor who clicks a link generated from a protocol-agnostic href (such as “//www.example.com”) if 1) my site is served by HTTPS and 2) example.com is not? I mean, have you guys even seen that screen? It’s not pleasant. It will make the visitor assume the worst about that other site, and by association, about my site since I sent them there.
Oh, I know what you’re going to say: just drop all links to all insecure sites. Well, in a lot of cases, that’s just not practical yet. After all, CSS-Tricks was insecure until, like, a month ago. Any solutions, besides everybody just moving to HTTPS at the same time?
I haven’t seen it! What browsers does it happen in? I just tried to replicate that scenario in Chrome but it didn’t seem to do anything. I know there are a lot of browsers out there, and I can do testing, but I’ve literally never seen it before so I figured it would be best to get more info from you first.
Chris, you may want to check out this plugin that will log any mixed content in a table in your dashboard:
https://wordpress.org/plugins/https-mixed-content-detector/
Honestly not trying to be a troll but just thought you should know that according to mister Irish, protocol agnostic url’s are now an inti-pattern.
Darn you Siri!! anti-pattern
I’m afraid I’m going to have to disagree on that one. Just YESTERDAY, my SSL stuff failed on this site. I had to go back to HTTP for a bit for the site to work at all. If all those articles has HTTPS links in them, they would have been broken because they are hosted right here, the same site where HTTPS was broken. If those images had hard-coded HTTPS images in them, they would have been broken even when the site was on HTTP.
I know what Paul is saying, but I think it’s slightly more complicated. I think if your FORCE HTTPS, protocol relative is fine. But if you offer your site either way and are super confident your SSL will always work 100% (hard bet), then hard coding HTTPS links might be smart.
I did notice your site was not working correctly yesterday. As per another comment this is one of the issues with “SSL everywhere” – I think we can expect to see more sites going wrong more often, in particular people not renewing their certs on time.
I realise that if we didn’t do stuff just because there’s more to go wrong we’d all still be traveling around by horse and cart. But for non ecommerce sites and sites not dealing with personal information I can’t really see the benefits.
Reader Josiah Altschuler wanted to point out about the .htaccess stuff: