3
votes

I'm attempting to redirect www to non-www for both HTTP and HTTPS requests. My root .htaccess looks like this:

RewriteEngine on

RewriteCond %{HTTP_HOST} ^www.example.com$
RewriteRule ^(.*)$ http://example.com/$1 [R=301]

RewriteCond %{HTTP_HOST} ^www.example.com$
RewriteCond %{SERVER_PORT} ^443
RewriteRule ^(.*)$ https://example.com/$1 [R=301]

This isn't fully working as expected. What happens:

Visiting http://www.example.com results in a redirect to http://example.com. This indicates my first rule and condition are working, the mod_rewite module is hunky-dory and .htaccess is enabled OK.

Visiting https://www.example.com doesn't result in a redirect. I remain on https://www.example.com

My question

In order for the above rewrite rules to work, must my server have an SSL certificate? It currently doesn't and I'm wondering if that is the reason things aren't working.

3
Don't think you need both rules... You're only checking the HTTP_HOST for the presence of www.. It should work for HTTPS as well.Mike Rockétt
@MikeRockett Right but you need to explicitly add the protocol http|https to rewriterule target. I'm not sure whats the dynamic way to deal with this.Rahil Wazir
@RahilWazir - Of course, that makes sense then.Mike Rockétt
@MikeRockett yeah, the first rule's target is the problem with that. E.g. How do you avoid hard-coding the http or https part?henrywright

3 Answers

1
votes

The first rule is taking precedence over https request because it simply met the rewrite condition. The first rule basically tells that match the domain and you can have your rewriterule to kick off. Instead add another condition which tells if its not https request

So try this:

RewriteEngine on

RewriteCond %{HTTP_HOST} ^www.example.com$
RewriteCond %{SERVER_PORT} !^443
RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]

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

You need ssl certificate for https protocol to work

Also I've added [L] flag which tells to not process further rules

3
votes

Perhaps you could try the following:

RewriteEngine on

# Check if the host contains "www."
RewriteCond %{HTTP_HOST} ^www\.

# Check if we're using HTTPS
RewriteCond %{HTTPS}s ^on(s)|off
RewriteCond http%1://%{HTTP_HOST} ^(https?://)(www\.)?(.+)$

# Redirect accordingly
RewriteRule ^ %1%3%{REQUEST_URI} [R=301,L]
1
votes

Inspired by @MikeRockétt's answer, this is a "simplified" version, using just one condition (instead of three). This also canonicalises a FQDN (ie. by removing an optional trailing dot on the hostname):

RewriteEngine On

RewriteCond %{HTTP_HOST}#%{HTTPS}s ^www\.(.+?)\.?#(?:on(s)|)
RewriteRule ^ http%2://%1%{REQUEST_URI} [R=301,L]

This 301 redirects www to non-www while maintaining the same protocol, HTTP or HTTPS. Can be used unaltered in either .htaccess (directory context) or in the main server config / vhost. (Although, if you are in a virtualhost context you should probably be using a simpler mod_alias Redirect instead.)

Explanation:

  • The TestString %{HTTP_HOST}#%{HTTPS}s (which evaluates to a string of the form "www.example.com#ons") is compared against the CondPattern ^www\.(.+?)\.?#(?:on(s)|)

    • The condition is successful for any request whose Host header starts "www.".

    • ^www\.(.+?)\.? first checks that the Host starts with "www." (if not, it fails early) and captures the remaining host header (excluding an optional trailing dot) in the %1 backreference. The regex that captures the domain name, ie. (.+?) is made non-greedy so that it does not capture the optional dot at the end.

    • # is just an arbitrary character that is used to separate the two values HTTP_HOST and HTTPS. Importantly, # cannot itself appear in either of these two values.
    • (?:on(s)|) is a non-capturing group that uses alternation to match against %{HTTPS}s. It either matches on(s) and captures the "s" (in the %2 backreference) or it matches anything and captures nothing (so %2 is empty).
  • Providing the previous condition is successful then the substitution string (http%2://%1%{REQUEST_URI}) is evaluated and the request redirected. The backreference %2, from the preceding CondPattern, either holds an "s" (to make "https" in the substitution) or is empty (ie. "http") and %1 is the hostname less the "www." prefix.

  • The REQUEST_URI server variable holds the full URL-path, including the slash prefix.

must my server have an SSL certificate?

Yes, otherwise the browser will block the connection (complaining about an invalid security certificate) - or simply time-out (because your server is not responding on port 443) and the request will not even reach your server.