fastboot-app-server icon indicating copy to clipboard operation
fastboot-app-server copied to clipboard

Common use case: force HTTPS via beforeMiddleware

Open mike-north opened this issue 8 years ago • 8 comments

Volunteering some example code, as a potential candidate for README. This uses the relatively new beforeMiddleware hook to force http traffic to https, with a provision for the EBS health check feature (which seems to come over HTTP unless HTTP is totally disabled for the env).


const enforceHTTPS = (req, res, next) => {
  // Header indicates edge server received request over HTTPS
  if (req.headers['x-forwarded-proto'] === 'https'){
    return next();
  } else {
    // Did not come over HTTPS. Fix that!
    return res.redirect(301, join(`https://${req.hostname}${req.url}`));
  }
};

let server = new FastBootAppServer({
  ...
  beforeMiddleware(app) {
    app.use((req, res, next) => {
      if (process.env.DISABLE_FORCE_HTTPS || // Ability to disable force HTTPS via env
          req.headers['user-agent'].indexOf('HealthChecker') >= 0) { // EBS health over HTTP
        return next(); // Proceed as planned (http or https -- whatever was asked for)
      } else {
        return enforceHTTPS(req, res, next); // Middleware to force all other HTTP --> HTTPS
      }
    });
  }
});

mike-north avatar Nov 17 '16 01:11 mike-north

Isn't forcing HTTPS in the app server something that is more a leftover from Rails' early days when everyone used HTTPS only for some (sensitive) endpoints while nowadays everyone is (should be) using HTTPS only all of the time in which case nginx would just redirect all HTTP requests to HTTPS so that no requests that are not HTTPS would ever reach the app server?

I'm not saying this is not a problem people have, I'm only saying the answer to them should not be "here is how you enforce HTTPS in Express" but "here's how you fix your nginx config".

marcoow avatar May 18 '17 13:05 marcoow

100% agree with what @marcoow said, however it's not straight forward to get into nginx config (or even apache rewrite rules) when hosting on a Paas like heroku or elastic beanstalk. Related: https://github.com/heroku/heroku-buildpack-emberjs/issues/8

mike-north avatar May 18 '17 13:05 mike-north

Any updates on this? I'm currently using heroku-buildpack-emberjs and need to find a solution to force HTTPS.

jakeleboeuf avatar Jul 12 '17 18:07 jakeleboeuf

@jakeleboeuf I'm in the same situation. Have you found a solution? The only thing I can think of is downloading the buildback, manually using @mike-north 's solution, and then reuploading the custom buildpack to an s3 bucket to use with heroku. Very not ideal but I'm not sure there's another alternative yet?

ghost avatar Jul 17 '17 19:07 ghost

@jakeleboeuf @konnorbeard Found any other solutions?

FutoRicky avatar Mar 14 '18 16:03 FutoRicky

There's a solution for Heroku as discussed in this thread: https://github.com/heroku/heroku-buildpack-emberjs/issues/8

musaffa avatar May 11 '18 12:05 musaffa

what if I do it on nginx? I have nginx with next config:

server {
    listen       80 default_server;
    server_name  mysite.me;
    root   /usr/share/nginx/html;

    gzip on;
    gzip_comp_level 5;
    gzip_disable "msie6";
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;

    location /health_check {
        access_log off;
        return  200;
    }

    location / {
        if ($http_x_forwarded_proto != 'https') {
            return 301 https://$server_name$request_uri;
        }
        resolver 8.8.8.8;
        proxy_pass https://324234234234.eu-central-1.elb.amazonaws.com:3443;
        proxy_redirect     off;
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
    }
    
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
server {
    listen       80;
    server_name  www.mysite.me;
    return       301 https://mysite.me$request_uri;
}
server {
    listen       80;
    server_name  blog.mysite.me;
    return       301 https://mysite.me/blog$request_uri;
}

So Ngnix does redirect from http to https and proxy request to FastBootAppServer. But when FastBoot tries to redirect It adds port 3443 to host like it ignores request's header Host $host. How transitionTo works on FastBoot? How does FastBoot get host for redirecting? Please advise how to figure out the problem.

devdemi avatar Jul 19 '18 12:07 devdemi

To slightly expand upon what @mike-north wrote, we can have a slightly modified version that prefers HTTPS and naked domains.

For anyone like me having many headaches using Apache ProxyPass, if you decide to forgo Apache altogether.

const enforceHTTPS = (req, res, next) => {
  // Header indicates edge server received request over HTTPS
  if (req.headers['x-forwarded-proto'] === 'https') {
    return next();
  } else {
    return res.redirect(301, `https://${req.hostname}${req.url}`);
  }
};

const enforceNaked = (req, res, next) => {
  if(/^www\./.test(req.headers.host)) {
    res.redirect(req.protocol + '://' + req.headers.host.replace(/^www\./,'') + req.url,301);
  } else {
    next();
  }
}

let server = new FastBootAppServer({
  notifier: notifier,
  downloader: downloader,
  gzip: true,
  beforeMiddleware(app) {
    app.use((req, res, next) => {
      if (req.headers['user-agent'].indexOf('HealthChecker') >= 0) {
        return next(); // Proceed as planned (http or https -- whatever was asked for)
      } else {
        return enforceHTTPS(req, res, next); // Middleware to force all other HTTP --> HTTPS
      }
    });

    app.use((req, res, next) => {
      return enforceNaked(req, res, next);
    });
  }
});```

ArtOfSettling avatar Dec 11 '18 12:12 ArtOfSettling