Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
How I Got Node.js Talking on EC2's Port 80

The Problem

Standard practices say no non-root process gets to talk to the Internet on a port less than 1024. How, then, could I get Node talking on port 80 on EC2? (I wanted it to go as fast as possible and use the smallest possible share of my teeny tiny little micro-instance's resources, so proxying through nginx or Apache seemed suboptimal.)

The temptingly easy but ultimately wrong solution:

Alter the port the script talks to from 8000 to 80:

}).listen(80);

.. and run it as root:

sudo /usr/local/bin/node foo.js

This is a Bad Idea, for all the standard reasons. (Here's one: if Node has access to the filesystem for any reason, you're hosed.)

One possibly-right way:

Add a port forwarding rule via iptables.

Oh dear familiar feeling: you are a total n00b and know not one thing about iptables.

First, I listed the rules currently running on the NAT (Network Address Translation) table:

[ec2-user@ip-XX-XXX-XX-X ~]$ sudo iptables -t nat -L

Chain INPUT (policy ACCEPT)
target     prot opt source    destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source    destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source    destination

I saw nothing, so I felt free to add a rule forwarding packets sent to external port 80 to internal port 8000:

[ec2-user@ip-XX-XXX-XX-X ~]$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8000

When I listed again, I saw a new PREROUTING chain:

[ec2-user@ip-XX-XXX-XX-X ~]$ sudo iptables -t nat -L

Chain PREROUTING (policy ACCEPT)
target     prot opt source     destination         
REDIRECT   tcp  --  anywhere   anywhere     tcp dpt:http redir ports 8000 

I checked my Node script, which was running on port 8000, and (yes!) it was responding on port 80.

Fumbling

During my early attempts I screwed up a bunch of times. I removed busted rules by specifying the right table, the right chain, and the right line number, like so:

[ec2-user@ip-XX-XXX-XX-X ~]$ sudo iptables -t nat -D PREROUTING 1

This removed the first line from the PREROUTING chain in my nat table.

Careful, now....

I did not do this myself but throughout this process I had a very strong feeling I should be very careful not to screw up port 22, which was my only way in.

Acknowledgements:

@dfgonzalez

This comment has been minimized.

Show comment
Hide comment
@dfgonzalez

dfgonzalez Oct 11, 2011

Worked like a charm in aws, thank you.

Worked like a charm in aws, thank you.

@kentbrew

This comment has been minimized.

Show comment
Hide comment
@kentbrew

kentbrew Oct 11, 2011

Sweet. Happy to hear it!

Owner

kentbrew commented Oct 11, 2011

Sweet. Happy to hear it!

@tommedema

This comment has been minimized.

Show comment
Hide comment
@tommedema

tommedema Mar 5, 2012

Does everyone have this prerouting rule that you need to remove?

Also, is this the final and only command we need to run?

sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8000

Moreover, how did you save these settings? Are they still active on reboot?

Finally, does this even work on the local network? I cannot seem to make it work on localhost:80.

Does everyone have this prerouting rule that you need to remove?

Also, is this the final and only command we need to run?

sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8000

Moreover, how did you save these settings? Are they still active on reboot?

Finally, does this even work on the local network? I cannot seem to make it work on localhost:80.

@justinfreitag

This comment has been minimized.

Show comment
Hide comment
@justinfreitag

justinfreitag Mar 12, 2012

Another method is to use a load balancer instance to do the re-routing from 80 to 8000

Another method is to use a load balancer instance to do the re-routing from 80 to 8000

@PeterAronZentai

This comment has been minimized.

Show comment
Hide comment
@PeterAronZentai

PeterAronZentai Jun 21, 2012

Why not process.setgid?

Why not process.setgid?

@kentbrew

This comment has been minimized.

Show comment
Hide comment
@kentbrew

kentbrew Jul 20, 2012

This is super-outdated, folks. Chances are pretty good Node or Amazon has outgrown the need.

Owner

kentbrew commented Jul 20, 2012

This is super-outdated, folks. Chances are pretty good Node or Amazon has outgrown the need.

@raisch

This comment has been minimized.

Show comment
Hide comment
@raisch

raisch Jan 24, 2013

Nope, still required. It is standard practice to disallow non-root access to TCP/UDP ports <=1024.

raisch commented Jan 24, 2013

Nope, still required. It is standard practice to disallow non-root access to TCP/UDP ports <=1024.

@tsabat

This comment has been minimized.

Show comment
Hide comment
@tsabat

tsabat Apr 20, 2013

for those looking to do this permanently using chef, checkout the iptables recipes and http://tickets.opscode.com/browse/COOK-1004

tsabat commented Apr 20, 2013

for those looking to do this permanently using chef, checkout the iptables recipes and http://tickets.opscode.com/browse/COOK-1004

@xpe

This comment has been minimized.

Show comment
Hide comment
@xpe

xpe Dec 1, 2013

The simple-iptables cookbook is cleaner than the 'plain' iptables cookbook.

simple-iptables has an example along these lines, described as being useful for Tomcat. Full disclosure: I haven't got my Jetty machine to NAT from 80 to 8080 using that example yet.

xpe commented Dec 1, 2013

The simple-iptables cookbook is cleaner than the 'plain' iptables cookbook.

simple-iptables has an example along these lines, described as being useful for Tomcat. Full disclosure: I haven't got my Jetty machine to NAT from 80 to 8080 using that example yet.

@btaz

This comment has been minimized.

Show comment
Hide comment
@btaz

btaz Feb 2, 2014

Using Nginx in front of Node.js gives you additional advantages. You can use it to serve static content and let Node handle the dynamic content. This gives you a nice performance boost. You can also use it to act as a load balancer or proxy in front of multiple Node instances. This works great in a production setup and is very stabile.

btaz commented Feb 2, 2014

Using Nginx in front of Node.js gives you additional advantages. You can use it to serve static content and let Node handle the dynamic content. This gives you a nice performance boost. You can also use it to act as a load balancer or proxy in front of multiple Node instances. This works great in a production setup and is very stabile.

@sahanDissanayake

This comment has been minimized.

Show comment
Hide comment
@sahanDissanayake

sahanDissanayake Oct 4, 2014

@kentbrew I'm facing the same hard time of getting the nodejs ( SailsJS ) app to run on EC2. IP forwarding works perfectly. But breaks when I need to access the phpmyadmin.

Please let me know if you have a solution.

Thank you

@kentbrew I'm facing the same hard time of getting the nodejs ( SailsJS ) app to run on EC2. IP forwarding works perfectly. But breaks when I need to access the phpmyadmin.

Please let me know if you have a solution.

Thank you

@ccerrato147

This comment has been minimized.

Show comment
Hide comment
@ccerrato147

ccerrato147 Nov 16, 2014

Thanks! Worked like a charm!!!

Thanks! Worked like a charm!!!

@dvidsilva

This comment has been minimized.

Show comment
Hide comment
@dvidsilva

dvidsilva Dec 18, 2014

<3 I fought for hours with this stupid nginx, thanks for this!

<3 I fought for hours with this stupid nginx, thanks for this!

@silveur

This comment has been minimized.

Show comment
Hide comment
@silveur

silveur May 15, 2015

thanks so much

silveur commented May 15, 2015

thanks so much

@eladnava

This comment has been minimized.

Show comment
Hide comment
@eladnava

eladnava Jun 23, 2015

Excellent work, thanks for your post!

Here's my take on this using nginx as a reverse proxy:
https://eladnava.com/binding-nodejs-port-80-using-nginx/

Excellent work, thanks for your post!

Here's my take on this using nginx as a reverse proxy:
https://eladnava.com/binding-nodejs-port-80-using-nginx/

@Dinesh1991

This comment has been minimized.

Show comment
Hide comment
@Dinesh1991

Dinesh1991 Mar 4, 2016

When I use the below command, I am able to redirect from port 3000 to port 80, but I need to redirect to port 80 when a request is for any other port.

sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3000

When I use the below command, I am able to redirect from port 3000 to port 80, but I need to redirect to port 80 when a request is for any other port.

sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3000

@baodinh

This comment has been minimized.

Show comment
Hide comment
@baodinh

baodinh Mar 10, 2016

Thanks so much, i want to know what is disadvantage of this solution ?

baodinh commented Mar 10, 2016

Thanks so much, i want to know what is disadvantage of this solution ?

@Alagarasan

This comment has been minimized.

Show comment
Hide comment
@Alagarasan

Alagarasan Mar 19, 2016

Thanks man. A great solution. I was breaking my head. Wonderful 👍

Thanks man. A great solution. I was breaking my head. Wonderful 👍

@peturh

This comment has been minimized.

Show comment
Hide comment
@peturh

peturh Jun 7, 2016

Yup, this did it for me. Tried getting the load balancer up, men feels overkill for my solution.

peturh commented Jun 7, 2016

Yup, this did it for me. Tried getting the load balancer up, men feels overkill for my solution.

@josebrwn

This comment has been minimized.

Show comment
Hide comment
@josebrwn

josebrwn Sep 2, 2016

This solution works partially, but will not allow the node server to see the /CSS folder, so all formatting is lost. Same with images, etc.

josebrwn commented Sep 2, 2016

This solution works partially, but will not allow the node server to see the /CSS folder, so all formatting is lost. Same with images, etc.

@JustinWinthers

This comment has been minimized.

Show comment
Hide comment
@JustinWinthers

JustinWinthers Sep 29, 2016

If you're using AWS, a preferred way is to use cloudfront or an elastic load balancer. You can easily create a cloudfront distribution or classic elastic load balancer that will forward traffic to the port of your choice on your ec2 server. You can even create load balancers with only one ec2 instance if you don't want to use cloudfront. This in my opinion should be preferred as you can also quickly add a ssl certificate to your distribution or load balancer using the AWS certificate manager without any server configuration. Then just simply point your A record(s) to the public DNS for your distribution or load balancer. No code, no hacks, easy as pie.

JustinWinthers commented Sep 29, 2016

If you're using AWS, a preferred way is to use cloudfront or an elastic load balancer. You can easily create a cloudfront distribution or classic elastic load balancer that will forward traffic to the port of your choice on your ec2 server. You can even create load balancers with only one ec2 instance if you don't want to use cloudfront. This in my opinion should be preferred as you can also quickly add a ssl certificate to your distribution or load balancer using the AWS certificate manager without any server configuration. Then just simply point your A record(s) to the public DNS for your distribution or load balancer. No code, no hacks, easy as pie.

@rohitarjunagi

This comment has been minimized.

Show comment
Hide comment
@rohitarjunagi

rohitarjunagi Nov 3, 2016

Worked! Thanks much!

Worked! Thanks much!

@stavarengo

This comment has been minimized.

Show comment
Hide comment
@stavarengo

stavarengo Nov 19, 2016

Its important to notice that these configurations will be lost after system restarts.
@kentbrew, I added the instructions to make it persistent between restarts. What you think to add it here?
Checkout the updated .MD code.

stavarengo commented Nov 19, 2016

Its important to notice that these configurations will be lost after system restarts.
@kentbrew, I added the instructions to make it persistent between restarts. What you think to add it here?
Checkout the updated .MD code.

@artforlife

This comment has been minimized.

Show comment
Hide comment
@artforlife

artforlife Nov 22, 2016

This came useful. Much appreciated!

This came useful. Much appreciated!

@User5100

This comment has been minimized.

Show comment
Hide comment
@User5100

User5100 Nov 30, 2016

Very useful. Many thanks

Very useful. Many thanks

@rcarl94

This comment has been minimized.

Show comment
Hide comment
@rcarl94

rcarl94 Dec 2, 2016

Awesome stuff, thank you sir.

rcarl94 commented Dec 2, 2016

Awesome stuff, thank you sir.

@andfinally

This comment has been minimized.

Show comment
Hide comment
@andfinally

andfinally May 12, 2017

Thanks! Very handy.

Thanks! Very handy.

@FoxxMD

This comment has been minimized.

Show comment
Hide comment
@FoxxMD

FoxxMD Jun 19, 2017

Thanks @JustinWinthers classic ELB is exactly what I needed 👍

FoxxMD commented Jun 19, 2017

Thanks @JustinWinthers classic ELB is exactly what I needed 👍

@suman1459

This comment has been minimized.

Show comment
Hide comment
@suman1459

suman1459 Jul 19, 2017

When I use the below command, I am able to redirect from port 443 to port 8000, but I also want to redirect my port 80 to 8000 .

sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 8000

When I use the below command, I am able to redirect from port 443 to port 8000, but I also want to redirect my port 80 to 8000 .

sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 8000

@FerreiraRaphael

This comment has been minimized.

Show comment
Hide comment
@FerreiraRaphael

FerreiraRaphael Aug 7, 2017

awesome! thx a lot

awesome! thx a lot

@imZEH

This comment has been minimized.

Show comment
Hide comment
@imZEH

imZEH Sep 12, 2017

Is there a command that rerouting port from 8000 to 80 upon restart the instance ?
some says use UFW ?

Thanks

imZEH commented Sep 12, 2017

Is there a command that rerouting port from 8000 to 80 upon restart the instance ?
some says use UFW ?

Thanks

@sombek

This comment has been minimized.

Show comment
Hide comment
@sombek

sombek Sep 19, 2017

Working!!
Thank you it's really helpful.
spent the last night searching for this problem!

sombek commented Sep 19, 2017

Working!!
Thank you it's really helpful.
spent the last night searching for this problem!

@sombek

This comment has been minimized.

Show comment
Hide comment
@sombek

sombek Sep 19, 2017

@imZEH
Why you don't use the elastic IP instead?

sombek commented Sep 19, 2017

@imZEH
Why you don't use the elastic IP instead?

@DecentM

This comment has been minimized.

Show comment
Hide comment
@DecentM

DecentM Sep 20, 2017

I usually just give the node binary permission to open a service port with sudo setcap 'cap_net_bind_service=+ep' $(which node)
(or replace $(which node) with wherever your node binary is if that returns a symlink)

DecentM commented Sep 20, 2017

I usually just give the node binary permission to open a service port with sudo setcap 'cap_net_bind_service=+ep' $(which node)
(or replace $(which node) with wherever your node binary is if that returns a symlink)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment