Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Https redirection #778

Closed
jozzhart opened this issue Apr 18, 2013 · 16 comments
Closed

Https redirection #778

jozzhart opened this issue Apr 18, 2013 · 16 comments
Labels
support Questions, discussions, and general support

Comments

@jozzhart
Copy link

Hello,

I'm using nodejitsu to host my app and I need to redirect all http requests to https at the server level.

https://www.nodejitsu.com/documentation/a-quickstart/faq/#how-do-i-force-my-clients-to-use-https-with-my-application

I'm not quire sure how to do this using hapi. I believe I should use the "onReqest' extension, but that only provides setUrl and setMethod methods. I need to force a redirect at this stage.

Can you please advise the best way to achieve this.

Thanks,
Jozz

@hueniverse
Copy link
Contributor

Create two servers and redirect all requests from one to the other:

var Hapi = require('hapi');
var http = new Hapi.Server(80);
var https = new Hapi.Server(443, { tls: {} });

var redirect = function () {

    this.reply.redirect('https://your.site/' + this.params.path);
});

http.route({ method: '*', path: '/{path*}', handler: redirect });

Note that this is using current 1.0 rc in master. In previous versions (0.16.0 from npm) use:

    this.reply.redirect('https://your.site/' + this.params.path).send();

@jozzhart
Copy link
Author

Thanks hueniverse for the prompt reply. Makes sense. Cheers Jozz

@chmanie
Copy link

chmanie commented May 20, 2014

How would I do that on heroku or openshift which assign just one port to your app and take care of the whole ssl business themselves? This is my solution so far but it seems rather hacky:

if ('production' === process.env.NODE_ENV || 'staging' === process.env.NODE_ENV) {
  this.server.ext('onRequest', function (request, next) {
    if (request.headers['x-forwarded-proto'] !== 'https') {
      request.originalPath = request.path;
      request.setUrl('/redirect');
    }
    next();
  });

  this.server.route([{
    method: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
    path: '/redirect',
    handler: function (request, reply) {
      var host = request.headers.host;
      reply().redirect('https://' + host + request.originalPath);
    }
  }]);
}

@hueniverse
Copy link
Contributor

@chmanie you can just redirect right from onRequest.

@chmanie
Copy link

chmanie commented May 21, 2014

You mean like this?

// redirect all http request to secure route
if ('production' === process.env.NODE_ENV || 'staging' === process.env.NODE_ENV) {
  server.ext('onRequest', function (request, reply) {
      if (request.headers['x-forwarded-proto'] !== 'https') {
        return reply('Forwarding to secure route')
          .redirect('https://' + request.headers.host + request.path);
      }
      reply();
    });
  }

@hueniverse
Copy link
Contributor

Yep.

@chmanie
Copy link

chmanie commented May 22, 2014

Thank you, this seems to work except I run into this issue when trying to access the non protected route: hapijs/yar#34

@carlo-m
Copy link

carlo-m commented Sep 18, 2014

@chmanie I have the same issue (single port), where exactly are you putting that code to handle the redirect?

@chmanie
Copy link

chmanie commented Sep 18, 2014

You basically can put it wherever you have access to the server object (you're not in express js 😄 ). But keep in mind that you have to use a yar version with the above issue resolved.

@carlo-m
Copy link

carlo-m commented Sep 22, 2014

@chmanie I did
server.ext('onRequest',function(request,next){ console.log('test'); next() });

When I enter https://localhost:8000 I see 'test' in the console. When I enter http://localhost:8000, I don't see anything in the console and in the browser I just see: 'waiting for localhost'.

@bendrucker
Copy link
Contributor

For anyone Googling this, I popped my redirect into a plugin:

https://github.com/bendrucker/hapi-require-https

@kristiankeane
Copy link

@carlo-m Did you ever find a solution to redirecting HTTP to HTTPS using a single port? I'm experiencing the exact same issue you described.

@bendrucker @chmanie I'm using code almost exactly like the examples on this page and the plugin - It works fine for a HTTPS request but the browser just hangs for a HTTP request - any ideas what I'm doing wrong? If you set up TLS on the connection maybe it ignores HTTP requests?

server.connection({
    port: 3000,
    host: localhost,
    tls: {
        key: fs.readFileSync(__dirname + '/tls/server.key', 'utf8'),
        cert: fs.readFileSync(__dirname + '/tls/server.crt', 'utf8')
    }
});

server.ext('onRequest', function (request, reply) {
    if (request.headers['x-forwarded-proto'] === 'http') {
        console.log('redirect to https');
        return reply().redirect('https://' + request.headers.host + request.url.path).code(301);
    }
    console.log('NO redirect');
    return reply.continue();
});

https://localhost:3000 // works fine
http://localhost:3000 // browser hangs, waiting for localhost

@chmanie
Copy link

chmanie commented Mar 13, 2015

@kristiankeane which version of hapi are you using?

@AdriVanHoudt
Copy link
Contributor

@chmanie I think @kristiankeane solved it with help from the gitter

@tengla
Copy link

tengla commented Aug 5, 2016

Usecase Hapi + Glue + Docker.

I made a rather elaborate example, to connect the dots, useful for noobs ;-)

You can make an entry in manifest.js, ie.:

const tls = {};
if (process.env.NODE_ENV === 'production') {
    tls.key  = Fs.readFileSync('/letsencrypt/etc/live/example.com/privkey.pem','utf8');
    tls.cert = Fs.readFileSync('/letsencrypt/etc/live/example.com/cert.pem','utf8');
}
...
connections: [{
    host: {
        $filter: 'env',
        production: '0.0.0.0',
        $default: 'localhost'
     },
     port: 3000,
     tls: {
         $filter: 'env',
         production: {
             key: tls.key,
             cert: tls.cert
         },
         $default: undefined
    },
    labels: ['api']
}, {
    port: 3001,
    labels: ['https-redirect']
}]

Register the plugin(https-redirect) in manifest.js:

plugin: {
    register: './plugins/https-redirect',
    options: {
        select: ['https-redirect'],
        method: '*',
        path: '/{path*}',
        uri: {
            $filter: 'env',
             production: 'https://production.example.com',
             $default: 'http://localhost:3000'
        }
    }
}

Write the plugin(./plugins/https-redirect.js) :

exports.register = function (servers, options, next) {

    const server = servers.select(options.select);
    /* Assumption: you already made sure your regular routes 
    * are not written to 3001-server.
    */
    server.route({
        method: options.method,
        path: options.path,
        handler: (request,reply) => {

            reply.redirect(options.uri + request.path);
        }
    });
    next();
};

exports.register.attributes = {
    name: 'https-redirect',
    version: '0.0.1'
};

Docker run:

// 'letsencrypt' is a volume with pre-made certificates that is mounted in container
docker run -it -d -p 443:3000 -p 80:3001 -v letsencrypt:/letsencrypt app-image

@captainjackrana
Copy link

I've created a simple hapi plugin for easy https redirections. You can find it here:
https://github.com/captainjackrana/hapi-gate

@lock lock bot locked as resolved and limited conversation to collaborators Jan 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
support Questions, discussions, and general support
Projects
None yet
Development

No branches or pull requests

9 participants