Really there’s a right way and a not-quite-as-right way to handle HTTPS on WordPress. It’s not that hard to do, and if your whole site is going to be HTTPS, then the easiest way is to change your home and site URLs to be https://example.com/
and put define( 'FORCE_SSL_ADMIN', true );
in your wp-config.php
file. Then you should (if this is an existing site) search your database for the old HTTP url and change that to HTTPS.
Seriously, that’s it. That tells WordPress to be HTTPS all the way and you’re done. Of course, that doesn’t actually work 100% for everyone, because there are some silly plugins and themes that do things like this:
add_action('wp_enqueue_scripts', 'enqueue_google_maps'); function enqueue_google_maps() { wp_enqueue_script('google-maps', 'http://maps.googleapis.com/maps/api/js?&sensor=false', array(), '3', true); }
The problem there is they’ve defined the script as HTTP and if your site is HTTPS then you’re going to get mixed content messages. And the real issue here is that means your connection is only partially encrypted! That non-encrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers. This, clearly, is not safe anymore. The right way to do your enqueues is with protocol relative URLs:
wp_enqueue_script('google-maps', '//maps.googleapis.com/maps/api/js?&sensor=false', array(), '3', true);
Alternately you can just use the HTTPS url, because that won’t break HTTP visits and it won’t make anything less secure.
But. Since you really can’t go in and edit all your themes and plugins, the plugin WordPress HTTPS is the way to go. That can force everything around. I know it’s not updated in a long time, but it still works. I keep thinking I’ll fork and clean it up… Well in my free time. The point of that plugin is that it lets you force everything to HTTPS, and will rewrite things on the fly. It’s a good idea.
Instead of using the plugin, I’ve seen a lot of people do this in their .htaccess
:
RewriteEngine On RewriteCond %{SERVER_PORT} 80 RewriteRule ^(.*)$ https://example.com/$1 [R,L]
In and of itself, this isn’t wrong. This forces everything HTTP to redirect to HTTPS. The problem is you’re still actually sending data from WordPress over HTTP first, and you’re right back to opening up to man-in-the-middle attacks because the data from WordPress goes from HTTP first and that’s, say it with me kids, insecure!
Now that said. This should be okay for most things. The POST calls should be sent securely, and all you should see on the return end is everything after that 301 redirect, but we can’t be absolutely sure about this. My buddy Jan used mod_substitute to force HTTPS (back before he moved to nginx). His code looks like this:
<Location /> AddOutputFilterByType SUBSTITUTE text/html Substitute "s|href="http://example.com/|href="https://example.com/|" Substitute "s|href='http://example.com/|href='https://example.com/|" Substitute "s|src=\'http:|src=\'|" Substitute "s|src=\"http:|src=\"|" </Location>
In doing this, he doesn’t need to worry about the HTTPS plugin I mentioned, because it forces everything with a src
attribute to be protocol relative. He also doesn’t have to search/replace his content if he doesn’t want to, which makes switching back easier. If you wanted to do that. But as Jan pointed out to me, he switched to nginx because it’s easier and supports variable substitutions.
Should you use .htaccess or nginx to force https instead of a plugin? That’s totally up to you. I use the plugin since I trust it to only mess with WordPress and not anything else I may have lying around. Also since my domains are often more than just WordPress, it’s a little easier for me to segregate their control. The flip side to this is that WordPress doesn’t redirect http traffic.
By this I mean if you turn your whole site to HTTPS properly, you can still go to http://example.com/this-is-a-page/
and WordPress will load it as HTTP. This is and is not a bug. WordPress is (properly) trusting your server to tell it what it should be. Your server is saying “Be HTTP or HTTPS! Whatever!” Now there is a trac ticket to have FORCE_SSL
really force SSL but that’ll be a while because there are a lot of complications in that change.
So yes, for now, I would use .htaccess
to add an extra later of SSL forcing, but with a bit of caution. If you’re proxying HTTPS (like you’re on a Varnish cache behind something like Pound or nginx) then you may need to use this code for your .htaccess redirect.
RewriteCond %{HTTP:X-Forwarded-Proto} !https RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
The reason for this is Apache can’t always see SSL if it’s not in charge of it (because it’s proxied or handled by a load balancer), and to teach it where it really lives. The trick there is the code I just showed you may not be right because every server’s a little different. There’s a great StackOverflow post on the problems of redirect loops while forcing https that you should read.
Good luck, and safe HTTPSing!