33

I have to redirect port 80 to 2368 in htaccess but I'd like to keep the requested protocol intact so that SSL doesn't break.

I currently have this:

RewriteCond %{HTTP_HOST} ^sub.domain.com$ [NC]
RewriteRule ^ http://sub.domain.com:2368%{REQUEST_URI} [P,QSA,L]

which works correctly but I'd like the protocol to be taken from the %{HTTP_HOST} condition if possible.

Is there a way to get this to be more dynamic without hard coding domains and protocols?

It seems very slow as is.

Tomas
  • 57,621
  • 49
  • 238
  • 373
secondman
  • 3,233
  • 6
  • 43
  • 66

5 Answers5

64

The trick provided by Jon is a nice hack, but I am afraid it could break if you want to use more [OR] conditions, and if you use more backreferences you have to be careful which number to use (%1 or %2 or ..).

I think the most elegant, robust and clean solution for smart HTTP/HTTPS handling is to set a variable:

# initialization code - put only once at the beginning of .htaccess
RewriteCond %{HTTPS} =on
RewriteRule ^(.*)$ - [env=proto:https]
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ - [env=proto:http]

# simply use %{ENV:proto} in your rules:
RewriteCond %{HTTP_HOST} ^sub.domain.com$ [NC]
RewriteRule ^            %{ENV:proto}://sub.domain.com:2368%{REQUEST_URI} [P,QSA,L]

The advantage also is that the initialization code needs to be run only once, so if you have more rewrite rules then the resultant code is much shorter and more elegant.

Note a common setup is https provided with cloudflare, in which case HTTPS is not 'on' on the server itself. In that case you need extra rules like (note order):

RewriteCond %{HTTP:CF-Visitor} '"scheme":"http"' [OR]
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ - [env=proto:http]
RewriteCond %{HTTP:CF-Visitor} '"scheme":"https"' [OR]
RewriteCond %{HTTPS} =on
RewriteRule ^(.*)$ - [env=proto:https]
pixelbeat
  • 30,615
  • 9
  • 51
  • 60
Tomas
  • 57,621
  • 49
  • 238
  • 373
  • 2
    In my case it was `%{ENV:HTTPS}` but other than that, great! For debugging you can just append `#%{HTTPS}-%{ENV:HTTPS}-%{ENV:proto}` to the redirection target and see where it goes. – frostschutz Oct 07 '16 at 12:41
  • 2
    The regex variable match and group in the RewriteRules that set the environment variable (`^(.*)$`) is excessive and should be removed, because it's not actually being used afterwards, instead the dash (-) is used to simply pass through. So for example all you need is `RewriteRule . - [env=...]` or similar. – Josip Rodin Mar 29 '17 at 18:13
  • VERY good idea to keep the proto in an env. This works well when you need multiple RewriteCond backreferences, but obviously that doesn't work. – Adambean Jun 29 '17 at 08:20
54

Starting with Apache 2.4 you can also use the variable %{REQUEST_SCHEME} to preserve the scheme:

RewriteRule "^" "%{REQUEST_SCHEME}://sub.domain.com:2368%{REQUEST_URI}" [P,QSA,L]

No need to meddle with any conditions this way.

Gerald Schneider
  • 17,416
  • 9
  • 60
  • 78
26

Try adding a condition like:

RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTPS}:s on:(s) 

Which checks that either HTTPS is off or it's on and you can use a backreference to fetch the "s" character:

RewriteCond %{HTTP_HOST} ^sub.domain.com$ [NC]
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTPS}:s on:(s) 
RewriteRule ^ http%1://sub.domain.com:2368%{REQUEST_URI} [P,QSA,L]

So if HTTPS is off, %1 is blank and the protocol is http://. If HTTPS is on, then the "s" is grouped and the %1 backreference is an "s", thus the protocol is https://.

However, this is all assuming that port 2368 can handle both unencrypted and SSL/TLS.

Jon Lin
  • 142,182
  • 29
  • 220
  • 220
  • 2
    Just a small comment. This is not a redirect, this is resolved by the server acting as a proxy (P flag). To do an actual redirect and ask the client to go to another URL you should use the R flag instead of P. – Ludecan Dec 14 '17 at 16:18
8

Or, you might use the solution posted as an answer by Jon Lin and rewrite it to use one RewriteCond and then set a variable as suggested in the answer by TMS.

That would look like this:

RewriteCond %{HTTPS}s ^(on(s)|offs)$
RewriteRule ^ - [env=s:%2]

RewriteCond %{HTTP_HOST} ^sub.domain.com$ [NC]
RewriteRule ^ http%{ENV:s}://sub.domain.com:2368%{REQUEST_URI} [P,QSA,L]

Or, if you prefer:

RewriteCond %{HTTPS}s ^(on(s)|offs)$
RewriteRule ^ - [env=proto:http%2]

RewriteCond %{HTTP_HOST} ^sub.domain.com$ [NC]
RewriteRule ^ %{ENV:proto}://sub.domain.com:2368%{REQUEST_URI} [P,QSA,L]
Community
  • 1
  • 1
-1

http to https redirection:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTPS} !on
    RewriteRule ^(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
</IfModule>

Non www to www redirection:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTP_HOST} !^www\. [NC]
    RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
</IfModule>

If you want to use both functionality, then place both above code chunks appropriately.

Yannis
  • 1,682
  • 7
  • 27
  • 45
Milan Krushna
  • 317
  • 2
  • 5