Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Connecting Ruby To Apache On My MacBook Pro Using A VirtualHost

By Ben Nadel on

This morning, as part of the exploration of my new MacBook Pro, I connected Python to the default installation of Apache. That ended up being a lot easier than I expected; as such, I wanted to see if I could do the same thing with Ruby. As it turns out, both Python and Ruby are languages that understand the CGI (Common Gateway Interface) standard. This made hooking up Ruby an almost step-by-step mimic of this morning's Python experience.

Just as before, the first thing I had to do was add a mapping to my hosts file (/etc/hosts) that would direct a special "ruby" url to my local host address:

Once this entry was added to my hosts file, all browser requests made for "" would evaluate to

Then, I added a VirtualHost entry in my Apache configuration that would map local requests for "" to a physical directory on my computer that I have set aside for Ruby experimentation:

  • <VirtualHost *:80>
  • # Hook virtual host domain name into physical location.
  • ServerName
  • DocumentRoot "/Sites/"
  • # Log errors to a custom log file.
  • ErrorLog /private/etc/apache2/logs/
  • # Add the ruby file extensions as a CGI script handler.
  • AddHandler cgi-script .rb
  • # Be sure to add ** ExecCGI ** as an option in the document
  • # directory so Apache has permissions to run CGI scripts.
  • <Directory "/Sites/">
  • Options Indexes FollowSymLinks MultiViews ExecCGI
  • AllowOverride None
  • Order allow,deny
  • Allow from all
  • </Directory>
  • </VirtualHost>

Just as with the Python configuration, this VirtualHost is performing two essential tasks: one, it's telling Apache to pipe Ruby file extensions (.rb) into the CGI script handler:

AddHandler cgi-script .rb

And two, it's granting Apache the appropriate permissions to execute CGI scripts (ExecCGI) in the given VirtualHost directory:

Options Indexes FollowSymLinks MultiViews ExecCGI

With Apache VirtualHost in place, we are now ready to execute our Ruby script files. I created a very simple "hello world" type example to see if everything was setup properly:

  • #! /usr/bin/ruby
  • # The preceeding line (starting with the shebang) is required to
  • # tell Apache where to find the Ruby interpreter for the
  • # following Ruby script. As far as I can tell, this has to be the
  • # first line in the file (even before any line breaks) otherwise it
  • # will throw a "Premature end of script headers" error.
  • # ------------------------------------------------------------ #
  • # ------------------------------------------------------------ #
  • # Define our collection of girls. With brackets, we can create
  • # implicit arrays in Ruby.
  • girls = [ "Tricia", "Sarah", "Joanna" ];
  • # ------------------------------------------------------------ #
  • # ------------------------------------------------------------ #
  • # Output the mime-type in the header. Notice that in Ruby, the
  • # puts()
  • puts( "Content-type: text/html" );
  • puts( "" );
  • # Output the page content. Notice that in Ruby, a string can
  • # naturally wrap multiple lines of code.
  • puts( "
  • <html>
  • <head>
  • <title>My First Ruby Script Running On Mac</title>
  • </head>
  • <body>
  • <h1>
  • My First Ruby Script Running On Mac
  • </h1>
  • <p>
  • Check out these stunning beauties:
  • </p>
  • <ul>
  • <li>#{girls[ 0 ]}</li>
  • <li>#{girls[ 1 ]}</li>
  • <li>#{girls[ 2 ]}</li>
  • </ul>
  • </body>
  • </html>
  • ");

In the above code, the first line is absolutely crucial:

#! /usr/bin/ruby

This "shebang" (sometimes referred to as a "pound-bang") tells the server where to find the Ruby interpreter. The server needs this binary in order to execute the rest of the code contained within the file.

It is also necessary that this directive appear as the very first line of code in the file. If you put so much as a line break before it, Apache will not know how to process the file and will result in a 500 Internal Server Error:

Premature end of script headers

NOTE: If you are getting a 401 Unauthorized error, you probably need to run a chmod command on the ruby script file to grant "everyone" read/write/execute permissions. I received the 401 error and ended up having to run "chmod 777 test.rb".

Once all of this was configured, I ran my Ruby code and got the following page output:

My First Ruby Script Running On Mac

Check out these stunning beauties:
* Tricia
* Sarah
* Joanna

Again, I'm just exploring the features of this Mac; while some of the usability of the Mac has left me a bit sore, it does seems to come with a ton of functionality baked-in. Playing around with Python and Ruby is actually helping me to become more comfortable with Apache and the greater machine configuration.

Reader Comments


I'm slowly starting to understand this UNIX beast. Thanks for the clarification on this. I am still a bit fuzzy on what exactly CGI is and how these various languages adhere to it (if that's even the correct concept). All I can reason is that CGI is a system interface that these script interpreters all know how to "compile" down to.

As a fellow CF dev, it will be interesting to follow your foray into ruby (ror too?), development. CF rocks, and ... It appears other langs may rock too ;o)


I'll probably stay away from anything "Rails" related at this time; that just seems like way more investment than I'd like to make. Mostly, I just want to see what kind of other points of view the various languages offer.

I think I stated it somewhere in another post, but I recently pre-ordered the book, "Seven Langauages In Seven Weeks," which provides a high-level view of some powerful languages and what features makes them so powerful. I'll be blogging about that journey, you can be sure!

@Ben - CGI - Common Gateway Interface. ColdFusion has a CGI scope that you can use to see the CGI variables. CGI is ancient tech (1993). Prior to PHP and ColdFusion (two of the earliest server side web languages. Sorry MS you were not even a glimmer in daddy Gate's eyes at this time) CGI was born. CGI is what is used by HTTP servers to talk to other server side tech.

I used to use canned PERL scripts to process form posts on websites back in those days. (1996) It was also used for all those Guest Books people used to put on their 'Animated GIF's From Hell Home Pages'. This was how data got to and from the back-end to the web page. Now we use a most enlightened approach and deploy ColdFusion.

Perl, Ruby, Python all have the SheBang as the first line of code. This means that the interpreter for those languages must be started and initiated at the beginning of each request. It's not like Java based J2EE or even .NET where the processing engine is always running and ready. In the olden days of ColdFusion, version 1.0, ColdFusion also used to work just like these interpreted scripts. Each request required the CF engine to start up and initialize prior to processing the request. ColdFusion had changed this behavior by 2.0 (I think.)

This is all stuff from the infancy of the WWW. The tech prior to that was even more fun. Ever manually send bytes of data from one computer to another via the parallel port and GWBasic? I did! One byte at a time. Had a sort of neat chat program running between two IBM-DOS computers back in 1987 and it mostly worked.


That's not how I remember connecting ruby. It went something like this... 2hours reading about passenger and mogul proxy. Finding a 3 year old guide for Mogul, giving up.

1 hour later finding a good guide on Passenger.

Another hour reading the passenger guide and trying to imitate the steps where applicable. Site comes up then I try to 'optimize', site breaks. Then I go back to using ruby for writing scripts and not web sites :(


Yeah, regarding the startup of the interpreter for each request - I was curious about that as I was coding it. I would say that seems like a really inefficient way of doing things; but, at the same time these scripts do seem super fast.

I wonder how applications like that maintain state across sessions? Perhaps it's all persisted with each request? Or maybe that's where the whole "Rails" stuff comes into play?

Anyway, those questions are more rhetorical than anything else.


I was surprised how easy it was. I am sure that if you have to deal with the "Rails" aspect, it becomes much more complicated. I am just lucky that I did the Python first as that sort of set the expectation of simplicity for the other CGI scripts. Now, I'm curious to see if this beast comes with any other programming languages baked in.

Like ColdFusion, it is best to run Python and Ruby web apps in an application server such as Rack. Rails and Sinatra, two popular web frameworks for Ruby, both use Rack under the hood as the application server.

No need for Apache to develop apps in Sinatra or Rails (you may want it for a production deploy, but there are a number of options besides Apache).


So, to see if I understand the parallels (bear with me - my server skills are new), are you saying that running Ruby on Rack is akin to running ColdFusion on JRUN?

Yep! Except that Rack is extensible and permits any Ruby app (including Sinatra and Rails apps) to hook into Rack and add their own middlewares and endpoints. When you run a Ruby app, your Ruby app is running in its own process and starts up its own Rack within the process, so your app has full control over that Rack.

It would kind of be like - if this were possible - you writing a Java class in your ColdFusion app and, on application startup, injecting your class into the JRun filter chain as a new middleware or endpoint, allowing you to inject extended behavior into the request-processing pipeline.


I sort of got that first paragraph; the second one went way over my head. But, I think I understand the concept in general. Having only run Ruby (or Python for that matter) as little one-off scripts, it never occurred to me that they could also execute as long-running processes.

Minor note about the permissions. Permissions for files in a website should 755. Owner gets RWX, Everyone else gets R-X. 777 is not a good idea. It means everyone can write to the file as well as the owner.