SSL/HTTPS with Zend Framework

I spent a fair amount of time today hunting for a decent way to force Zend Framework 1.11 to use SSL. Sadly, there was no simple answer that completely worked, so after scamming pieces parts from a number of Google searches I came up with my own solution.

The task, to force all HTTP calls to HTTPS, however, I might only want to secure selected modules/controllers in the future. My solution.

In configs/application.ini I added:

ssl.modules.require_ssl = 'all';
ssl.controllers.require_ssl = '';

This could just as easily be:

ssl.modules.require_ssl = 'module1,module2,module3';
ssl.controllers.require_ssl = 'controller11';

Now create a new plugin at plugins/Ssl.php

class Plugin_Ssl extends Zend_Controller_Plugin_Abstract
{
    
    public function preDispatch($request)
    {
        
        //get the config settings for SSL
        $options = new Zend_Config_Ini(APPLICATION_PATH.'/configs/application.ini');
      
        $secure_modules = explode(',',$options->production->ssl->modules->require_ssl); 
        $secure_controllers = explode(',',$options->production->ssl->controllers->require_ssl); 

        $front = Zend_Controller_Front::getInstance();
        $request = $front->getRequest();        
        $module = $request->getModuleName();
        $controller = $request->getControllerName();

        // Force SSL Only use it production environment
        if ( APPLICATION_ENV == 'production' )
        {
            if ($secure_modules[0] == 'all' || in_array(strtolower($module), $secure_modules) || in_array(strtolower($controller), $secure_controllers)) {
                if (!isset($_SERVER['HTTPS']) && !$_SERVER['HTTPS']) {
                    $url        = 'https://'
                                . $_SERVER['HTTP_HOST']
                                . $request->getRequestUri();
        
                    $redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
                    $redirector->gotoUrl($url);
                }
            }
        }
    }
} 

Finally in your bootstrap create the following function:

    protected function _initPlugins() {
        $frontController = Zend_Controller_Front::getInstance();
        $frontController->registerPlugin( new Plugin_Ssl());        
    }

This will execute the SSL test on every page load without having to add a call to every single controller.

That’s it. Hope it helps somebody.

Wait.. Why not just use Apache or .htaccess you ask? Because Zend’s .htaccess forces everything to index.php and the internal router gets stupid if you try and do it that way.

6 Responses

  1. Shaun Freeman July 31, 2012 / 7:17 am

    Thank you I wasted a lots of time trying to do this via apache.

    Works like a dream!

  2. Bernhard Werner March 22, 2013 / 6:24 am

    James, your solution works like a charm. Thanks a lot.

    I just want to propose one minor improvement. The following lines…

    $options =  Zend_Controller_Front::getInstance()->getParam('bootstrap')->getOptions();
    $secure_modules = explode(',',$options['ssl']['modules']['require_ssl']);
    $secure_controllers = explode(',',$options['ssl']['controllers']['require_ssl']);

    would eliminiate the need for parsing the application.ini a second time.

    Again, thanks for your piece of code!

  3. Sam May 29, 2013 / 4:31 pm

    I might be wrong, but I think that line 22 should read:

    if (!isset($_SERVER[‘HTTPS’]) || !$_SERVER[‘HTTPS’]) {

    Also, line 18 is not necessary. You can just override the ssl.* options in application.ini for each environment, without hard coding the behaviour.

  4. zendnewb November 13, 2013 / 9:42 am

    hey just wanted to report a small typ0. the folder is called “plugin” not “plugins” !
    thanks for the dope code 🙂

    • James December 14, 2013 / 4:31 pm

      No problem. Zend will actually accept either plugin or plugins for the core directories it understands.

Leave a Reply

Your email address will not be published. Required fields are marked *