1

I'm trying to reduce the amount of 301 redirect chaining on my server so I'd like to combine a subdomain to non-www redirect (except where the subdomain is dev) with a HTTP to HTTPS redirect (using %{HTTP:X-Forwarded-Proto}) as the instance is behind a load balancer.

Here's what I have so far in my .htaccess:

# move http to https 
RewriteCond %{HTTP:X-Forwarded-Proto} =http
RewriteRule .* https://%{HTTP:Host}%{REQUEST_URI} [L,R=301]

# Remove leading www 
RewriteCond %{HTTP_HOST} ^www.example.net [NC]
RewriteRule ^(.*)$ https://example.net/$1 [R=301,L]

There are three problems with my present implementation:

  1. A request to http://www.example.net will have two redirects.

  2. Like most www to non-www redirect examples on this site, it won't redirect ww. or wwww. so my analytics has lots of mistyped subdomains that haven't been redirected.

  3. I'd like to exclude the dev. subdomain from redirection, so http://dev.example.net and its https sibling, as I use dev. for development and release staging.

How should I go about combining this?

MrWhite
  • 12,647
  • 4
  • 29
  • 41
Matt Parkins
  • 145
  • 5

2 Answers2

2
# Remove leading www, always using https regardless of the current URL scheme
RewriteCond %{HTTP_HOST} ^w{2,4}.example.net(?::|$) [NC,NV]
RewriteRule .* https://example.net%{REQUEST_URI} [L,R=301]

# move http to https, except for dev.example.net
RewriteCond %{HTTP:X-Forwarded-Proto} =http [NC,NV]
RewriteCond %{HTTP_HOST} !^dev.example.net(?::|$) [NC,NV]
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
  1. Added NC (nocase) flag to the X-Forwarded-Proto condition, since its value seemed to be case-insignificant i.e. HTTP = Http = hTTp = http. You're free to remove this.
  2. Added NV (novary) flag to the X-Forwarded-Proto condition to hide it from the Vary response header. Again, you're free to remove this especially if it is not the case (the reverse proxy automatically filters it for you anyway before sending it to client) or needed for correct cacheing behaviour (the cache does not otherwise distinguish http and https content).
  3. Added NV (novary) flag to the Host header condition to hide it from the Vary response header. You may also remove this if you have a broken cache server.
  4. Made the RewriteRule flag order consistent (L,R=301 and R=301,L are essentially the same).
  5. Prevented www.example.net.but.not.actually.yours.com from being recognized, but still allows e.g. www.example.net:443.
  6. Consistently use %{REQUEST_URI} as the substitution.
  7. Also recognize ww and wwww.
Jin-oh Kang
  • 121
  • 2
1

1) A request to http://www.example.net will have two redirects.

This can be resolved by simply reversing the two rules. Then www.example.net is redirected to HTTPS in the first redirect, so the HTTP to HTTPS redirect does not need to trigger.

(This does, however, assume you have no intention of implementing HSTS - in which case you would need to keep them as two redirects since redirecting to HTTPS on the same hostname first is a requirement.)

2) Like most www to non-www redirect examples on this site, it won't redirect ww. or wwww. so my analytics has lots of mistyped subdomains that haven't been redirected.

Ordinarily, requests to ww. or wwww. subdomains simply won't resolve, so this is not normally an issue. For this to work you have to have configured a wildcard subdomain in DNS and configured the server to accept such requests.

But this can be accounted for by modifying the regex (snippet) from ^www\. to ^w{2,4}\..

3) I'd like to exclude the dev. subdomain from redirection, so http://dev.example.net and its https sibling, as I use dev. for development and release staging.

This only applies to the HTTP to HTTPS rule, so an additional condition can be applied here to exclude hostnames that start dev..

Bringing the above points together, try the following:

# Remove leading ww, www or wwww (and redirect to HTTPS)
RewriteCond %{HTTP_HOST} ^w{2,4}\.example\.net [NC]
RewriteRule (.*) https://example.net/$1 [R=301,L]

# Move http to https (except dev subdomain)
RewriteCond %{HTTP:Host} !^dev\. [NC]
RewriteCond %{HTTP:X-Forwarded-Proto} =http
RewriteRule ^ https://%{HTTP:Host}%{REQUEST_URI} [R=301,L]

I've kept your use of HTTP:Host the same (in order to access the Host HTTP request header) in case this is a requirement of the load balancer? Otherwise, it's more common to use the HTTP_HOST server variable here.

The ! prefix on the CondPattern (ie. !^dev\.) negates the regex, so the condition is successful when the Host does not start with dev.. (I assume www.dev. isn't a thing?)

(.*) is the same as ^(.*)$ since the regex is greedy by default.

You will need to clear your browser cache before testing. It is advisable to first test with 302 (temporary) redirects in order to avoid any caching issues.

MrWhite
  • 12,647
  • 4
  • 29
  • 41