wiki:RedirectHttpsRequests
Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Version 1 (modified by plewis, 13 years ago) (diff)

--

Making HTTP and HTTPS Redirects Work

This guide is designed for TurboGears version 0.9.

The Problem

One common setup is to have a reverse proxy (like Pound, Lighttpd, or Apache) sit in front of CherryPy and handle requests. If you want to handle both http and https protocols, you set up your reverse proxy to deal with the secure communications, and then pass types of both types of requests (secure and insecure) to CherryPy as a normal http request. CherryPy processes the requests, returns them to the proxy, and the proxy passes them on to the client (secure or insecure, depending on the original request).

This causes a problem if you do a HTTPRedirect to a url in your application. CherryPy thinks that this is an unencrypted request. So, the redirect url provided by CherryPy will begin with http, regardless of whether or not the original url was http or https.

One Solution

One way to work around this is to have the proxy provider provide a hint to CherryPy as to the original protocol. Most proxies have some way to set a custom header to the request for passing it on. So, the general solution is to add a special header to https requests, and then have a CherryPy filter look for that header and modify the base url to something more appropriate.

Step One: Make the proxy set a custom header

I'm going to use  Pound as my example reverse proxy. I configure Pound to add a special header called 'X-Requested-Ssl'. The relevant part of my pound configuration file looks like:

ListenHTTPS
	Address 127.0.0.1
	Port 443
	Cert "/Users/plewis/Desktop/Pound-2.0.2/mycert.pem"
	AddHeader "X-Requested-Ssl: Yes"
	Service
		BackEnd
			Address 127.0.0.1
			Port 8080
		End
	End
End

Lighttpd users can set a custom header via  setenv.add-request-header. Apache users can use  mod_headers. You don't to add this header on a global basis, but only for https requests.

Step Two: Create a new filter

Now, we create a special CherryPy filter to search for the "X-Requested-Ssl" header. Below is one file that will do the trick:

httpsfilter.py

from cherrypy.filters.basefilter import BaseFilter
import cherrypy

class HTTPSFilter(BaseFilter):
   
    def before_request_body(self):
        if not cherrypy.config.get('https_filter.on', False):
            return
            
        request = cherrypy.request
        
        new_baseurl = cherrypy.config.get('https_filter.secure_base_url', 
                                            'https://localhost')
        
        # Check for a special header 'X-Requested-Ssl'.  If we have it,
        # then we substitute the secure base url  
        if request.headers.has_key('X-Requested-Ssl') \
                        and request.headers['X-Requested-Ssl'].upper() == 'YES':
            #cherrypy.log("HTTPSFilter has a request for ssl.")
            request.base = new_baseurl

When the filter finds the 'X-Requested-Ssl' header, it sets the base url of the request with the secure url.

Step Three: Set configuration file settings

In config.py, set the following configuration variables:

path('/')
https_filter.on = True
https_filter.secure_base_url = "https://secure.example.com"

Set the secure_base_url value to be the base site name of your reqest.

Step Four: Use the filter

Assuming you want to use this filter throughout your application, add the follwing to your root controller:

from httpsfilter import HTTPSFilter

class Root(turbogears.controllers.RootController):
    
    _cp_filters = [HTTPSFilter()]
    # Rest of controller code ...

This is the super-secret semi-undocumented way to add a custom filter to you controller.

Step Five: Test things out

You should be able to add a method to your controller like:

    @turbogears.expose()
    def test_redirect(self):
        turbogears.controllers.redirect("/") #redirect uses cherrypy.HTTPSRedirect

Calling this url should work over both http and https, and will properly redirect to the base of your application.

Better Solutions

In the long run, it would be better if CherryPy were able to listen on two different ports for requests. If we could configure the ports to have different base urls, it would make processing redirects much easier. It looks like this may be possible to do in upcoming versions of TurboGears.