Deployment of Static Files to Heroku

If you use Ruby on Rails, Django or NodeJS frameworks on the same domains they have special folders, usually called static, public or assets. But what if you what deploy just “static” file which are usually not so static, by they way.

Heroku uses Cedar stack as an application creation tool and it doesn’t support flat-out deployment of static files such as HTML, CSS or JavaScript, unless they come with some server-side language, e.g, PHP, Ruby, Python. This might come in handy if you are using front-end applications build with jQuery or BackboneJS framework and services like Parse.com or Firebase.com or consume your own back-end deployed as a different application on different instance and/or domain.

There are multiple ways to trick Heroku and Cedar into thinking that your HTML, CSS and JavaScript files are PHP or Ruby on Rails, or any other legitimate Cedar stack, applications. Here is the simplest way is to create index.php file in your project folder, on the same level as your .git folder. You can do this in terminal with this command:

$ touch index.php

Then we turn off PHP with .htaccess directive. You can add line to .htaccess and create it with this terminal command:

$ echo 'php_flag engine off' > .htaccess

This two terminal command will create empty index.php file and .htaccess file which turns PHP off. This solution was discovered by Kenneth Reitz.

Another approach is less elegant but also involves PHP. Create file index.php on the same level as index.html in the project folder which you would like to publish/deploy to Heroku with the following content:

<?php echo file_get_contents('index.html'); ?>

Third way is to use Ruby and Ruby Bamboo stack. In this case, we would need the following structure:

 -project folder
    config.ru
   /public
      index.html
      /css
      app.js
      ...

The path in index.html to CSS and other assets should be relative. i.e. ‘css/style.css’. The config.ru file should contain the following code:

use Rack::Static, 
  :urls => ["/stylesheets", "/images"],
  :root => "public"

run lambda { |env|
  [
    200, 
    {
      'Content-Type'  => 'text/html', 
      'Cache-Control' => 'public, max-age=86400' 
    },
    File.open('public/index.html', File::RDONLY)
  ]
}

For more details, you could refer to official Bamboo Heroku documentation.

Last but not least, for Python and Django developers, you could add following to your urls.py:

urlpatterns += patterns(”, (r’^static/(?P.*)$’, ‘django.views.static.serve’, {‘document_root’: settings.STATIC_ROOT}),)

Or with this Procfile line:

web: python my_django_app/manage.py collectstatic --noinput; bin/gunicorn_django --workers=4 --bind=0.0.0.0:$PORT my_django_app/settings.py 

The full Django post is at Managing Django Static Files on Heroku.

If you use NodeJS, here is how to write your own server:

var http = require("http"),
url = require("url"),
path = require("path"),
fs = require("fs")
port = process.argv[2] || 8888;

http.createServer(function(request, response) {

  var uri = url.parse(request.url).pathname
    , filename = path.join(process.cwd(), uri);

  path.exists(filename, function(exists) {
    if(!exists) {
      response.writeHead(404, {"Content-Type": "text/plain"});
      response.write("404 Not Found\n");
      response.end();
      return;
    }

    if (fs.statSync(filename).isDirectory()) filename += '/index.html';

    fs.readFile(filename, "binary", function(err, file) {
      if(err) {        
        response.writeHead(500, {"Content-Type": "text/plain"});
        response.write(err + "\n");
        response.end();
        return;
      }

      response.writeHead(200);
      response.write(file, "binary");
      response.end();
    });
  });
}).listen(parseInt(port, 10));

console.log("Static file server running at\n  => http://localhost:" + port + "/\nCTRL + C to shutdown");