Apache and Node.js on the Same Server

Apachenode.js

Apache Problem Overview


I want to use Node because it's swift, uses the same language I am using on the client side, and it's non-blocking by definition. But the guy who I hired to write the program for file handling (saving, editing, renaming, downloading, uploading files, etc.), he wants to use apache. So, I must:

  1. Convince him to use Node (he's giving up little ground on that)

  2. Figure out how to upload, download, rename, save, etc. files in node or

  3. I must install apache and node on the same server.

Which is the most favorable situation, and how do I implement that?

Apache Solutions


Solution 1 - Apache

Great question!

There are many websites and free web apps implemented in PHP that run on Apache, lots of people use it so you can mash up something pretty easy and besides, its a no-brainer way of serving static content. Node is fast, powerful, elegant, and a sexy tool with the raw power of V8 and a flat stack with no in-built dependencies.

I also want the ease/flexibility of Apache and yet the grunt and elegance of Node.JS, why can't I have both?

Fortunately with the ProxyPass directive in the Apache httpd.conf its not too hard to pipe all requests on a particular URL to your Node.JS application.

ProxyPass /node http://localhost:8000

Also, make sure the following lines are NOT commented out so you get the right proxy and submodule to reroute http requests:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

Then run your Node app on port 8000!

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello Apache!\n');
}).listen(8000, '127.0.0.1');

Then you can access all Node.JS logic using the /node/ path on your url, the rest of the website can be left to Apache to host your existing PHP pages:

enter image description here

Now the only thing left is convincing your hosting company let your run with this configuration!!!

Solution 2 - Apache

This question belongs more on Server Fault but FWIW I'd say running Apache in front of Node.js is not a good approach in most cases.

Apache's ProxyPass is awesome for lots of things (like exposing Tomcat based services as part of a site) and if your Node.js app is just doing a specific, small role or is an internal tool that's only likely to have a limited number of users then it might be easier just to use it so you can get it working and move on, but that doesn't sound like the case here.

If you want to take advantage of the performance and scale you'll get from using Node.js - and especially if you want to use something that involves maintaining a persistent connection like web sockets - you are better off running both Apache and your Node.js on other ports (e.g. Apache on localhost:8080, Node.js on localhost:3000) and then running something like nginx, Varnish or HA proxy in front - and routing traffic that way.

With something like varnish or nginx you can route traffic based on path and/or host. They both use much less system resources and is much more scalable that using Apache to do the same thing.

Solution 3 - Apache


Instructions to run node server along apache2(v2.4.xx) server:

In order to pipe all requests on a particular URL to your Node.JS application create CUSTOM.conf file inside /etc/apache2/conf-available directory, and add following line to the created file:

ProxyPass /node http://localhost:8000/

Change 8000 to the prefered port number for node server.
Enable custom configurations with following command:

$> sudo a2enconf CUSTOM

CUSTOM is your newly created filename without extension, then enable proxy_http with the command:

$> sudo a2enmod proxy_http

it should enable both proxy and proxy_http modules. You can check whether module is enabled or not with:

$> sudo a2query -m MODULE_NAME

After configuration and modules enabled, you will need to restart apache server:

$> sudo service apache2 restart

Now you can execute node server. All requests to the URL/node will be handled by node server.

Solution 4 - Apache

Running Node and Apache on one server is trivial as they don't conflict. NodeJS is just a way to execute JavaScript server side. The real dilemma comes from accessing both Node and Apache from outside. As I see it you have two choices:

  1. Set up Apache to proxy all matching requests to NodeJS, which will do the file uploading and whatever else in node.

  2. Have Apache and Node on different IP:port combinations (if your server has two IPs, then one can be bound to your node listener, the other to Apache).

I'm also beginning to suspect that this might not be what you are actually looking for. If your end goal is for you to write your application logic in Nodejs and some "file handling" part that you off-load to a contractor, then its really a choice of language, not a web server.

Solution 5 - Apache

You can use a different approach such as writing a reverse proxy server with nodejs to proxy both apache and all other nodejs apps.

First you need to make apache run on a different port other than port 80. ex: port 8080

Then you can write a reverse proxy script with nodejs as:

var proxy = require('redbird')({port: 80, xfwd: false);
 
proxy.register("mydomain.me/blog", "http://mydomain.me:8080/blog");
proxy.register("mydomain.me", "http://mydomain.me:3000");

Following article describes the whole process of making this.

RUN APACHE WITH NODE JS REVERSE PROXY – USING REDBIRD

Solution 6 - Apache

I combined the answer above with certbot SSL cert and CORS access-control-allow-headers and got it working so I thought I would share the results.

Apache httpd.conf added to the bottom of the file:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

Apache VirtualHost settings (doc root for PHP is under Apache and SSL with Certbot, while node.js/socket.io site runs on port 3000 - and uses SSL cert from Apache) Also notice the node.js site uses the proxy for the folder /nodejs, socket.io, and ws (websockets):

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName www.example.com
    ServerAlias www.example.com
    DocumentRoot /var/html/www.example.com
    ErrorLog /var/html/log/error.log
    CustomLog /var/html/log/requests.log combined
    SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

    RewriteEngine On
    RewriteCond %{REQUEST_URI}  ^socket.io          [NC]
    RewriteCond %{QUERY_STRING} transport=websocket [NC]
    RewriteRule /{.*}       ws://localhost:3000/$1  [P,L]

    RewriteCond %{HTTP:Connection} Upgrade [NC]
    RewriteRule /(.*) ws://localhost:3000/$1 [P,L]

    ProxyPass /nodejs http://localhost:3000/
    ProxyPassReverse /nodejs http://localhost:3000/

    ProxyPass /socket.io http://localhost:3000/socket.io
    ProxyPassReverse /socket.io http://localhost:3000/socket.io

    ProxyPass /socket.io ws://localhost:3000/socket.io
    ProxyPassReverse /socket.io ws://localhost:3000/socket.io

</VirtualHost>
</IfModule>

Then my node.js app (app.js):

var express = require('express');
var app = express();
	app.use(function(req, res, next) {
		res.header("Access-Control-Allow-Origin", "*");
		res.header("Access-Control-Allow-Headers", "X-Requested-With");
		res.header("Access-Control-Allow-Headers", "Content-Type");
		res.header("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS");
		next();
	});
var http = require('http').Server(app);
var io = require('socket.io')(http);

http.listen({host:'0.0.0.0',port:3000});

I force a ip4 listener, but that is optional - you can substitute:

http.listen(3000);

node.js app (app.js) code continues with:

io.of('/nodejs').on('connection', function(socket) {
	//optional settings:
    io.set('heartbeat timeout', 3000); 
	io.set('heartbeat interval', 1000);

    //listener for when a user is added
    socket.on('add user', function(data) {
         socket.join('AnyRoomName');
         socket.broadcast.emit('user joined', data);
    });

    //listener for when a user leaves
    socket.on('remove user', function(data) {
         socket.leave('AnyRoomName');
         socket.broadcast.emit('user left', data);
    });

	//sample listener for any other function
    socket.on('named-event', function(data) {
         //code....
         socket.broadcast.emit('named-event-broadcast', data);
    });
    
    // add more listeners as needed... use different named-events...
});

finally, on the client side (created as nodejs.js):

//notice the /nodejs path
var socket = io.connect('https://www.example.com/nodejs');

//listener for user joined
socket.on('user joined', function(data) {
    // code... data shows who joined...
});

//listener for user left
socket.on('user left', function(data) {
    // code... data shows who left...
});

// sample listener for any function:
socket.on('named-event-broadcast', function(data) {
    // this receives the broadcast data (I use json then parse and execute code)
    console.log('data1=' + data.data1);
    console.log('data2=' + data.data2);
});

// sample send broadcast json data for user joined:
socket.emit('user joined', {
    'userid': 'userid-value',
 	'username':'username-value'
});

// sample send broadcast json data for user left 
//(I added the following with an event listener for 'beforeunload'):
// socket.emit('user joined', {
//     'userid': 'userid-value',
//     'username':'username-value'
// });

// sample send broadcast json data for any named-event:
socket.emit('named-event', {
    'data1': 'value1',
 	'data2':'value2'
});

In this example when the JS loads, it will emit to the socket a "named-event" sending the data in JSON to the node.js/socket.io server.

Using the io and socket on the server under path /nodejs (connected by client), receives the data an then resends it as a broadcast. Any other users in the socket would receive the data with their listener "named-event-broadcast". Note that the sender does not receive their own broadcast.

Solution 7 - Apache

ProxyPass /node http://localhost:8000/     
  • this worked for me when I made above entry in httpd-vhosts.conf instead of httpd.conf
  • I have XAMPP installed over my environment & was looking to hit all the traffic at apache on port 80 with NodeJS applicatin running on 8080 port i.e. http://localhost/[name_of_the_node_application]

Solution 8 - Apache

I recently ran into this kinda issue, where I need to communicate between client and server using websocket in a PHP based codeigniter project.

I resolved this issue by adding my port(node app running on) into Allow incoming TCP ports & Allow outgoing TCP ports lists.

You can find these configurations in Firewall Configurations in your server's WHM panel.

Solution 9 - Apache

I was looking for the same information. Finally found the answer from the link on the answer above by @Straseus

http://arguments.callee.info/2010/04/20/running-apache-and-node-js-together/

Here is the final solution to run apache website on port 80, node js service on port 8080 and use .htaccess RewriteRule

In the DocumentRoot of the apache website, add the following:

Options +FollowSymLinks -MultiViews

<IfModule mod_rewrite.c>

RewriteEngine on

# Simple URL redirect:
RewriteRule ^test.html$ http://arguments.callee.info:8000/test/ [P]

# More complicated (the user sees only "benchmark.html" in their address bar)
RewriteRule ^benchmark.html$ http://arguments.callee.info:8000/node?action=benchmark [P]

# Redirect a whole subdirectory:
RewriteRule ^node/(.*) http://arguments.callee.info:8000/$1 [P]

For the directory level redirect, the link above suggested (.+) rule, which requires one or more character after the 'node/'. I had to convert it to (.*) which is zero or more for my stuff to work.

Thanks a lot for the link @Straseus

Solution 10 - Apache

I am assuming that you are making a web app because you refer to Apache and Node. Quick answer - Is it possible - YES. Is it recommended - NO. Node bundles it's own webserver and most websites run on port 80. I am also assuming that there is currently no Apache plugin which is supported by Nodejs and I am not sure if creating a virtual host is the best way to implement this. These are the questions that should be answered by developers who maintain Nodejs like the good folks at Joyent.

Instead of ports, it would be better to evaluate Node's tech stack which is completely different from most others and which is why I love it but it also involves a few compromises that you should be aware of in advance.

Your example looks similar to a CMS or a sharing web app and there are hundreds of out of the box apps available that will run just fine on Apache. Even if you do not like any readymade solution, you could write a webapp in PHP / Java / Python or mix n match it with a couple of ready made apps and they are all designed and supported to run behind a single instance of Apache.

It's time to pause and think about what I just said.

Now you are ready to decide on which techstack you are going to use. If your website will never use any out of the thousands of ready made apps that require Apache, then go for Node otherwise you must first eliminate the assumptions that I have stated earlier.

In the end, your choice of techstack is way more important than any individual component.

I completely agree with @Straseus that it is relatively trivial to use node.js file system api for handling uploads and downloads but think more about what you want from your website in the long run and then choose your techstack.

Learning Node's framework is easier than learning other frameworks but it is not a panacea. With a slightly more effort (which may be a worthwhile endeavor in itself), you can learn any other framework too. We all learn from each other and you will be more productive if you are working as a small team than if you are working alone and your backend technical skills will also develop faster. Therefore, do not discount the skills of other members of your team so cheaply.

This post is about a year old and chances are that you have already decided but I hope that my rant will help the next person who is going through a similar decision.

Thanks for reading.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMattView Question on Stackoverflow
Solution 1 - ApacheSteven de SalasView Answer on Stackoverflow
Solution 2 - ApacheIain CollinsView Answer on Stackoverflow
Solution 3 - ApachekrmldView Answer on Stackoverflow
Solution 4 - ApacheYarek TView Answer on Stackoverflow
Solution 5 - ApachewathmalView Answer on Stackoverflow
Solution 6 - ApacheDr. Aaron DishnoView Answer on Stackoverflow
Solution 7 - Apacherahul shuklaView Answer on Stackoverflow
Solution 8 - ApacheParitosh PandeyView Answer on Stackoverflow
Solution 9 - Apachepd1980View Answer on Stackoverflow
Solution 10 - ApacheRHTView Answer on Stackoverflow