There are scenarios where it might be prudent to support HTTP request redirection on a single port, and thus, a single virtual server. Yes, this can be done with the alias port zero, but that locks all other ports down unless you plan to build out a pretty extensive iRule to support the various services required for each port. This latter option is less than ideal. So what can be done?

TL;DR - only two steps required. First, check the Non-SSL Connections box in the SSL profile. Second, create an iRule to redirect non-SSL connections to SSL.

The Details

I have a test virtual server, appropriately called testvip, and on that testvip I have no iRules and a clientssl profile attached with only my certificate key chain changed from the parent profile.

ltm virtual testvip {
    ip-protocol tcp
    pool testpool
    profiles {
        cssl {
            context clientside
        http { }
        tcp { }
    source-address-translation {
        type automap
    translate-address enabled
    translate-port enabled
    vs-index 50
ltm profile client-ssl cssl {
    app-service none
    cert myssl.crt
    cert-key-chain {
        myssl {
            cert myssl.crt
            key myssl.key
    chain none
    defaults-from clientssl
    inherit-certkeychain false
    key myssl.key
    passphrase none

In this configuration, I expect that normal HTTPS requests will work just fine, and HTTP requests will fail. Let's take a look with curl, first with the working HTTPS and then the failing HTTP: (leaving curl details out for brevity here)

### HTTPS ###
MY-MAC:~ rahm$ curl -v -s -k https://test.test.local/ 1> /dev/null
-> HTTP/1.1 200 OK

### HTTP ###
MY-MAC:~ rahm$ curl -v -s -k http://test.test.local:443 1> /dev/null
-> Empty reply from server

Now that we have confirmed that the regular HTTP request is not working, let's enable non-SSL connections. Again, that's done with the checkbox in the clientssl profile:

Nonssl connections

Now let's try that second test again.

### HTTP ###
MY-MAC:~ rahm$ curl -v -s -k http://test.test.local:443 1> /dev/null
-> HTTP/1.1 200 OK

That's pretty cool, we successfully made an HTTP request on the HTTPS single-port virtual server! But that's not the endgame, and is most certainly not a desired state. Let's take the final step with the iRule to capture any non-SSL connections and redirect them. Here's the iRule:

  set https_state 1
  if { ![info exists https_state] } {
    HTTP::redirect https://[HTTP::host][HTTP::uri]

This is optimized slightly from the original iRule from gasch that inspired this article. First, we look at the CLIENTSSL_HANDSHAKE event and set a variable to true to capture that this connection is indeed an SSL connection. Then in the HTTP_REQUEST event, if that variable doesn't exist, we know that the connection is not SSL and we take the redirect action. Simple and sleek! Now, with that iRule applied, let's test again:

### HTTP ###
MY-MAC:~ rahm$ curl -v -s -k http://test.test.local:443 1> /dev/null
* Rebuilt URL to: http://test.test.local:443/
*   Trying
* Connected to test.test.local ( port 443 (#0)
> GET / HTTP/1.1
> Host: test.test.local:443
> User-Agent: curl/7.54.0
> Accept: */*
* HTTP 1.0, assume close after body
< HTTP/1.0 302 Found
< Location: https://test.test.local:443/
< Server: BigIP
* HTTP/1.0 connection set to keep alive!
< Connection: Keep-Alive
< Content-Length: 0
* Connection #0 to host test.test.local left intact

And there it is! You can see the HTTP request is accepted as previously, but instead of the 200 OK status, you get the 302 Found redirect. Sometimes things like this are solutions looking for problems, but conserving ports, IP space, configuration objects, etc, could all be factors.