Redirect HTTP to HTTPS with .htaccess (301 redirect)

Published on Updated on 4 min read

Visitors still landing on the unencrypted page? A few lines in the .htaccess file are enough to redirect every HTTP request permanently to HTTPS with a 301 redirect.

Your server already delivers the website over HTTPS, but visitors still end up on the unencrypted HTTP version? Then a redirect is missing. In this guide you set up a permanent redirect (status code 301) from HTTP to HTTPS with an .htaccess file.

A 301 redirect is also the right choice from an SEO point of view: search engines transfer the rankings of the old HTTP address to the HTTPS address instead of treating both variants as separate pages.

Requirements

  • Your server runs Apache. nginx uses a different configuration, more on that further below.
  • A valid SSL certificate is already in place. If not, you'll find the matching guide in the article Set up a free SSL certificate with Let's Encrypt.
  • You have access to the web directory of your server, either via SSH or via FTP.

Important: only set up the redirect once HTTPS actually works. Otherwise you send visitors to an address that greets them with a certificate warning.

Enable mod_rewrite and allow .htaccess

The redirect relies on the Apache module mod_rewrite. Enable it and restart Apache:

a2enmod rewrite
systemctl restart apache2

For Apache to read the .htaccess file at all, the VirtualHost has to permit it. Check the configuration under /etc/apache2/sites-available/ to see whether AllowOverride All is set for your directory:

<Directory /var/www/MeineDomain.de>
    AllowOverride All
    Require all granted
</Directory>

If it says AllowOverride None there, your .htaccess file is silently ignored. After a change to the VirtualHost file, Apache needs a restart.

Create the .htaccess file

Change into the directory that holds your website:

cd /var/www/<YOUR-WEB-DIRECTORY>

Open the .htaccess file in an editor. If it does not exist yet, the command creates it:

nano .htaccess

Add the following lines. This variant checks directly whether the connection is encrypted and appends the complete requested path to the target:

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Save the file with "CTRL + X", then "Y" and "Enter".

Alternative using the server port

On older configurations where the HTTPS variable is never set, checking the port works instead:

RewriteEngine On
RewriteCond %{SERVER_PORT} !^443$
RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]

Variant behind a reverse proxy or load balancer

If a reverse proxy or a load balancer sits in front of your Apache and terminates the encryption itself, the request reaches Apache unencrypted. The rule above would then redirect endlessly. In that case, evaluate the header that the proxy sends along:

RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Test the redirect

Open your domain in the browser over http://. You should land on https:// right away. On the command line you check the status code directly:

curl -I http://MeineDomain.de

The output has to show HTTP/1.1 301 Moved Permanently, plus a Location line with the HTTPS address. If a status code 302 appears instead, check that the rule really contains R=301.

Enable HSTS (optional)

With HTTP Strict Transport Security you tell the browser never to call your domain over HTTP again. That removes the first unsecured request. Start by enabling the required module:

a2enmod headers
systemctl restart apache2

Then add the following to the .htaccess file:

<IfModule mod_headers.c>
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</IfModule>

Keep in mind: HSTS commits you for a full year. As long as the value is stored in the browser, the domain can no longer be reached over HTTP. Only set the header once HTTPS runs reliably and all subdomains have a valid certificate as well.

Common errors

  • Redirect loop ("too many redirects"): almost always the result of a proxy in front of the server. Use the variant with X-Forwarded-Proto.
  • Nothing happens: either mod_rewrite is not active, or the VirtualHost does not allow an .htaccess file (AllowOverride None).
  • Error 500 after saving: a typo in the .htaccess file. The exact reason is in the Apache error log under /var/log/apache2/error.log.
  • Mixed content: the page does load over HTTPS, but it still pulls in images or scripts over HTTP. Adjust those addresses in your source code or in the database.
  • File not found: the .htaccess file starts with a dot and is therefore hidden. ls -la shows it.

Redirect without .htaccess

An .htaccess file is read again on every single request. If you have access to the server configuration, it is faster to put the rules straight into the VirtualHost. For the HTTP VirtualHost this is enough:

<VirtualHost *:80>
    ServerName MeineDomain.de
    Redirect permanent / https://MeineDomain.de/
</VirtualHost>

If you use nginx instead of Apache, there are no .htaccess files. There you add the redirect to the server block for port 80:

server {
    listen 80;
    server_name MeineDomain.de www.MeineDomain.de;
    return 301 https://$host$request_uri;
}

Frequently asked questions

Why a 301 redirect and not a 302?
301 stands for a permanent redirect. Search engines use it to transfer the value of the previous HTTP address to the HTTPS address. A 302 counts as temporary and does not pass those signals on.
My .htaccess file is ignored. What causes that?
Either mod_rewrite is not enabled, or the VirtualHost does not allow overrides. Set AllowOverride All for your directory and restart Apache afterwards.
Why do I end up in a redirect loop?
That happens almost every time a reverse proxy or load balancer in front of Apache terminates the encryption. In that case evaluate the header X-Forwarded-Proto instead of the HTTPS variable.
Does this work with nginx as well?
No, nginx does not know .htaccess files. There you add the redirect directly to the server block for port 80, with the line return 301 https://$host$request_uri.
Should I enable HSTS on top of this?
HSTS increases security, but it locks you in for the configured lifetime. Only enable it once HTTPS works reliably on the domain and on all subdomains.

.htaccess Redirect 301 redirect HTTPS Apache mod_rewrite HSTS