Apache – Why Forcing Redirect with Absolute URL

apache-2.2mod-rewrite

Context

With the following rule:

# Redirect root url to /tvs
RewriteRule ^/$ /tvs [R=301,L]

I have an absolute redirection like:

HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Fri, 24 Mar 2017 16:42:23 GMT
Content-Type: text/html; charset=iso-8859-1
Content-Length: 312
Location: http://www.tvsvizzera.it/tvs
Vary: Accept-Encoding
X-Node: pcache02
X-Cached: MISS
Proxy-Connection: Keep-Alive
Connection: Keep-Alive

and in the rewrite.log I have the following

10.101.114.1 - - [24/Mar/2017:16:43:34 +0100] [www.tvsvizzera.it/sid#7fd446c22168][rid#7fd446d6eb48/initial] (2) init rewrite engine with requested uri /
10.101.114.1 - - [24/Mar/2017:16:43:34 +0100] [www.tvsvizzera.it/sid#7fd446c22168][rid#7fd446d6eb48/initial] (2) rewrite '/' -> '/tvs'
10.101.114.1 - - [24/Mar/2017:16:43:34 +0100] [www.tvsvizzera.it/sid#7fd446c22168][rid#7fd446d6eb48/initial] (2) explicitly forcing redirect with http://www.tvsvizzera.it/
tvs
10.101.114.1 - - [24/Mar/2017:16:43:34 +0100] [www.tvsvizzera.it/sid#7fd446c22168][rid#7fd446d6eb48/initial] (1) escaping http://www.tvsvizzera.it/tvs for redirect
10.101.114.1 - - [24/Mar/2017:16:43:34 +0100] [www.tvsvizzera.it/sid#7fd446c22168][rid#7fd446d6eb48/initial] (1) redirect to http://www.tvsvizzera.it/tvs [REDIRECT/301]

Why does apache explicitly force the redirect to be absolute ?

Problem

The server is behind a reverse proxy doing the SSL Offloading.
So if the redirection would stay relative the same rule would work for both protocol HTTP/HTTPS. But this isn't the case and when requesting with https, one gets redirected to http.

I know I can change the rule to be something like this

RewriteRule ^/$ %{ENV:REQUEST_SCHEME}://%{HTTP_HOST}/tvs [R=permanent,L]

But I wanted to understand this behavior.

Thanks for any explanation.

Best Answer

All external redirects (R flag) result in mod_rewrite requiring an absolute URL. When you don't explicitly include the scheme and hostname in the RewriteRule substitution then Apache will use the current protocol, server name and port. Apache (or strictly speaking, mod_rewrite) doesn't send a relative URL back in the Location: HTTP response header hoping that the user-agent will resolve the URL.

(It was not until June 2014 (RFC 7231) that relative URLs in the Location: header officially became part of the standard. So, particularly if you're still on Apache 2.2, then it's difficult to argue that Apache is doing anything wrong here.)

If Apache is sending back an HTTP URL (as opposed to HTTPS) then it would seem that Apache is serving the response back over HTTP, not HTTPS.

Yes, you could manually "fix" the URL using mod_rewrite. However, the REQUEST_SCHEME server variable is likely to have the same "problem". If you are behind a proxy then you might need to check the X-Forwarded-Proto header (or similar) as to whether it's "http" or "https".

As well as (conditionally) forcing HTTPS in the directive itself, you can also force HTTPS in the server config with the ServerName (and UseCanonicalName) directive(s). However, I assume that is not desirable and you need to be flexible?

You can also edit the Location: header, before it is sent back to the client. Using mod_headers you can force a relative URL by manually stripping out the scheme + hostname, as suggested in this StackOverflow answer:

Header edit Location "^https?://[a-zA-Z0-9.-]+" ""

Further reference: