tl;dr
If you’re trying to configure nginx on Elastic Beanstalk to redirect http requests to https, here’s what I learned.
- During deployment, the nginx configuration for your app is located at this file path:
/tmp/deployment/config/#etc#nginx#conf.d#00_elastic_beanstalk_proxy.conf
via - Using a container command, you can edit that nginx configuration file right before it gets deployed.
- I used a little perl one-liner to insert the redirect.
container_commands: | |
10redirect: | |
command: perl -0777 -pe 's#(listen 8080;\n)#$1\n if (\$request_uri = "/health") {\n set \$redirectFlag N;\n }\n\n if (\$http_x_forwarded_proto != "https") {\n set \$redirectFlag "\${redirectFlag}Y";\n }\n\n if (\$redirectFlag = "Y") {\n return 301 https://\$host\$request_uri;\n }#igms' -i /tmp/deployment/config/#etc#nginx#conf.d#00_elastic_beanstalk_proxy.conf |
# the container command will insert this snippet immediately after | |
# listen 8080; | |
# We don't want to redirect health check traffic from the Elastic Load Balancer | |
# or it will think our app is not healthy | |
if ($request_uri = "/health") { | |
set $redirectFlag N; | |
} | |
# And nginx doesn't allow nested or compound conditionals | |
# So we build up this "redirectFlag" variable as a string | |
if ($http_x_forwarded_proto != "https") { | |
set $redirectFlag "${redirectFlag}Y"; | |
} | |
# And now we know: if the value is exactly "Y", it is safe to redirect | |
# If the traffic is from the load balancer, which we don't want to redirect, | |
# the value will be "NY", which will not match | |
if ($redirectFlag = "Y") { | |
return 301 https://$host$request_uri; | |
} |
Background
So... we're using Amazon Web Services Elastic Beanstalk for one of the apps I'm working on. It's pretty easy to get started, but it's also really easy to find that you’re fighting Elastic Beanstalk to get it to stop doing something stupid.
I was fighting one of those "stupid" things the other day: http-to-https redirect.
Let's say you have a web application that requires users to login with a name and a password. You don't want users' passwords getting sent over the internet without being encrypted, of course. So you enable SSL and serve content over https.
But sometimes, users type your domain name (like, “google.com”) into the address bar, which defaults to http. Or they follow a link to your app that mistakenly uses http instead of https. In any event, you don’t want users who are trying to get to your app to get an error message telling them there’s nothing listening on the other end of the line, so you need to be listening for http requests but redirecting them to https for security.
Now, our app is written in Node.js, and we’ve configured Elastic Beanstalk to point internet traffic to an Elastic Load Balancer, which terminates SSL and proxies traffic to the backing servers, which are running our app behind nginx. This might sound like too many levels of indirection, but nginx is optimized for serving static content, while Node.js is optimized for dynamic content, so this is a pretty common setup.
And this is where Elastic Beanstalk gets stupid.
When we configured our app to listen for both http and https traffic, Elastic Beanstalk directed all of that traffic to nginx — and configured nginx to direct all of that traffic to our app — without giving us any way to redirect http traffic to https.
I imagine lots of apps want to respond to both http and https traffic while redirecting insecure http requests to secure https requests. Maybe I’m wrong.
Anyway, I want to do that. And I found it insanely difficult to accomplish.
After a couple of days trying to do the same thing I found this post and it worked perfectly!
Thank you so much!