Apache: insecure request sent to secure port...want to redirect

12,239

I think this is probably a bug in how Apache 2.2 and lower handles this particular circumstance.

It seems that on an SSL read 400 Bad Request error, Apache 2.2 doesn't return the HTTP response code or headers, only the HTTP response body. I test by telnetting to port 443 and sending:

GET / HTTP/1.1

The server immediately returns (for me):

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
Reason: You're speaking plain HTTP to an SSL-enabled server port.<br />
Instead use the HTTPS scheme to access this URL, please.<br />
<blockquote>Hint: <a href="https://server.tld/"><b>https://server.tld/</b></a></blockquote></p>
</body></html>

Note the lack of an HTTP response code or any HTTP headers.

When I do this to an Apache 2.4 server, I get:

HTTP/1.1 400 Bad Request
Date: Sun, 10 Feb 2013 00:47:23 GMT
Server: Apache/2.4.3 (Unix) OpenSSL/1.0.0g
Content-Length: 462
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
Reason: You're speaking plain HTTP to an SSL-enabled server port.<br />
Instead use the HTTPS scheme to access this URL, please.<br />
</p>
<hr>
<address>Apache/2.4.3 (Unix) OpenSSL/1.0.0g Server at server.tld Port 443</address>
</body></html>

If I setup an ErrorDocument line like you did:

ErrorDocument 400 https://server.tld/

Then I get the HTML for a 302 redirect, but again, none of the headers. Without the 302 redirect response code and the Location: header, the browser won't redirect.

Try upgrading to Apache 2.4 and see if it works. I've tested and confirmed with at least Apache 2.4.3, but I haven't gone through the effort of finding exactly when this was behavior was updated. I suspect that in the large amount of work they did preparing 2.4, they corrected the undesirable behavior as a side-effect.

Relevant Apache httpd bugs:

UPDATE

You can force a buggy Apache to give you the behavior you want (the redirect) by having the script print out its headers (which will not be sent to the client), and then manually print out again the headers you want. Here's a basic Perl script that works under Apache 2.2.22:

#!/usr/bin/perl

use strict;
use CGI;

my $q = CGI->new();

# this will cause Apache to handle the response properly, but is meaningless otherwise
print $q->redirect("https://localhost/");

# this actually performs the redirect
print "HTTP/1.1 302 Found\r\n";
print "Location: https://localhost/\r\n";
print "\r\n";

# you can do whatever you want here; this will be the HTML body

You should be aware that there are other reasons a 400 may be generated besides just talking to an SSL port without SSL. The easy way to determine this is to look for the HTTPS environment variable. If it's set, then SSL was negotiated properly and something else is causing the 400 (don't do the double header trick if that's the case). If HTTPS is not set, return your redirect as above.

Share:
12,239

Related videos on Youtube

Keith Becker
Author by

Keith Becker

Systems Admin. Network Nerd. Mac Geek. Developer. personal Twitter Tumblr Facebook about.me professional LinkedIn Careers 2.0 code CoRD SourceForge GitHub BitBucket

Updated on September 18, 2022

Comments

  • Keith Becker
    Keith Becker almost 2 years

    Preface

    Firstly: A simply Port 80 -> Port 443 Rewrite WILL NOT fix this. In almost every previous question, mail thread, forum thread, etc., I have found this was the first ignorant response and was parroted several times.

    Secondly: Yes I know you cannot serve HTTP and HTTPS traffic on the same port. This is not that.

    Scenario:

    Apache Server hosting multiple sites via port multiplication. Port 80 serves a public site. Port 443 serves the secure version of that site.

    Ports 7443, 8443, and 9443 each serve separate SSL-Secured sites.

    If a user mistypes the URL, or is given a link that isn't valid, say http://hostname.tld:7443, they are given the following ridiculous page:

    Apache Bad Request error message

    Instead of the server just redirecting them over to https://hostname.tld:7443.

    My Question is, how in the name of Zeus's butthole can you modify Apache's behavior or this error message to redirect the user automagically?

    Apache is obviously serving a non-https request (to display that error message) even though it is configured for HTTPS. It seems remarkably dumb to me to not just do the redirect by default, but I can understand why they went with the behavior they did, even if I don't agree with it. So my question is: can you change it? They are handling the error SOMEWHERE, and with Apache being the configuration cornucopia that it is, it stands to reason there is some directive somewhere to handle this behavior, but I have been unable to find it in several hours of tinkering so far.

    Update:

    I have attempted a variety of things including:

    • using ErrorDocument 400 directives to get to a CGI and a PHP script that just send Status 301 and Location headers. This results in a blank page. Using ErrorDocument 400 https://hostname.tld:7443 simply results in that link being displayed on the page.

    • Using almost every combination of mod_rewrite I or Google can come up with, including blanket statements that direct the site entirely; these never work. Literally, they do nothing. I'm surmising that Apache is kicking to the above error before even attempting to process the rewrite directives.

    I can't use port-based redirects, because of the custom port use. I can't use script-based redirects because they never get served because of the http/https mismatch. I'm almost willing to chalk this up to a bug, or an unintended behavior, but somebody had the forethought to put a very custom error message in there, they didn't bother thinking that maybe you'd want to just cart to the URL they are already providing?

    • Daniel Vérité
      Daniel Vérité over 11 years
    • Keith Becker
      Keith Becker over 11 years
      Daniel, That was one of the many questions I had found and solutions I had attempted before posting this question. I think the problem was the PHP component, but since @hrunting's answer's update is working beautifully for me at the moment, I'm not going to sink any more time into it than I already have; at least not until I have to.
    • michele b
      michele b almost 11 years
      upvote for the linked video, it conveys my emotions perfectly
  • trent
    trent over 11 years
    Hrm so I assume you have started from the top of you apache config file and worked your way down to see what it is calling
  • Keith Becker
    Keith Becker over 11 years
    I have practically memorized the entire sites-available directory. Even if you just redirect it to nonsense, or redirect it to a different site, different port, etc., it just keeps loading that 400 Bad Request.
  • Keith Becker
    Keith Becker over 11 years
    Holy. Crap. Perfect Answer. Gracias.
  • hrunting
    hrunting over 11 years
    You know, as I read this, I think to myself, "Man, it really sucks if you have Apache 2.2 and you want this kind of redirect behavior." I'm not going to go through the effort of finding an appropriate Apache 2.4 deb (or making my own) for my Ubuntu system. I bet you can hack a solution from your PHP script. Apache's clearly just dumping the output to the client, so if you return the specific expected content, I bet you can get the behavior you're looking for without upgrading Apache. Try having your PHP script print out "HTTP/1.1 302 Found\r\nLocation: server.tld\r\n\r\n".
  • Keith Becker
    Keith Becker over 11 years
    The problem there is that it doesn't appear that the PHP script is getting processed. I should note that this is current sitting on Apache 2.2.22 on Ubuntu Server 12.04. And yes, it really, really sucks; especially when you note that in those bug reports, they seem to be completely set against even providing an option to just automatically do the redirect, and there doesn't appear to be much hope that 2.2 will ever be getting the fix.
  • Keith Becker
    Keith Becker over 11 years
    And like I said up above, it just doesn't make sense to me why you go through the trouble to write that error message, rather than redirecting to secure (seriously, why not just redirect? the more I think about it, the less I think that is a bad idea).
  • hrunting
    hrunting over 11 years
    Are you sure the PHP script isn't getting processed? Because you get no output instead of the standard output, I bet it is getting processed. My bet is that it is getting processed, it's just not dumping out exactly the right data that's necessary (using PHP function calls probably assume that the web server is going to do the "right thing" which it isn't in this case). I bet if the header content were output as the body instead of normal headers, it would work.
  • hrunting
    hrunting over 11 years
    Technically, it is a 400 bad request. The server can't know exactly what the client was doing. You might redirect them to server.tld when they were trying to reach otherserver.tld which would have been served properly if HTTPS would have been used with SNI. You just don't know, and the server has no way of knowing. Every web server does pretty much the same thing (although they get the response code and headers right).
  • Stefan Lasiewski
    Stefan Lasiewski over 11 years
    Wow great bug! Great find! I wonder if this is different with SNI, since SNI allows the request to be read before the SSL Handshake.
  • hrunting
    hrunting over 11 years
    I don't think so. In Apache, it's basically bailing when the SSL context can't be established, and when it kicks it back, the information related to the SSL context (which is where any SNI information would be) is lost.
  • Keith Becker
    Keith Becker over 11 years
    I don't disagree that is a 400 Bad Request. But with almost every other freaking error message/status/code I can dictate what Apache should do. Here, in a case where that ability is, in my eyes, paramount, they kneecap you with a lame message, and don't even handle it properly so that you can't override it.
  • Keith Becker
    Keith Becker over 11 years
    I have grepped the entire file system looking for the text of that bloody message to see if I can where they are leeching it from, on the off chance I could inject a <javascript tag and get lucky with hacking redirection that way, but thus far, no dice. Nothing about this seems to be handled in a way that follows the otherwise pretty rigid Apache awesomeness...
  • Keith Becker
    Keith Becker over 11 years
    I added a comment to the 50823 bug, just to put my thoughts on the record there. The bug is still open, so perhaps hope still exists...
  • hrunting
    hrunting over 11 years
    I added an update to the answer that should do what you need it to do.
  • Keith Becker
    Keith Becker over 11 years
    So I see, thanks! I'll try to get it tested later today / this evening and see if I can get it working.
  • Keith Becker
    Keith Becker over 11 years
    Works beautifully man. Dumped that into a redirect.pl in the cgi-bin and set up the corresponding ErrorDocument 400. If I could accept this twice I totally would. Thanks so much for your help, and kudos for being a better Apache Ninja than me :)
  • hrunting
    hrunting over 11 years
    No problem. That was a fun problem to work on.