Share localhost with SSH & Apache
There's been a lot of services released lately that aim to assist developers share their local development environments over the internet. localtunnel, showoff.io and pagekite come to mind, just to name a few. However, you can roll your own forwarding service if you have SSH access to a server running Apache fairly easily.
The Big Picture
First, setup a webserver to listen for connections to demo.example.com. When a user connects to the webserver, it will attempt to proxy their request to port 1337 (demo.example.com port 1337).
Second, a reverse proxy initiated with SSH on your localhost creates a connection to demo.example.com, and then has all traffic on demo.example.com port 1337 forwarded to localhost port 80.
User => demo.example.com:80 <=> demo.example.com:1337 <=> localhost:80
Requirements
Server
- Apache Web Server
- Apache mod_proxy module
- OpenSSH
Client
- Local web application
- SSH client
- SSH public/private keys to login to the remote server
Server Setup (Ubuntu)
You should be able to login to your server over SSH using public/private key authentication. How to do that is out of scope for this post, however there are plenty of guides available online that explain in detail how to do this.
Apache: Enable mod_proxy
$ sudo a2enmod proxy_http
Apache: Configure VirtualHost
Setup Apache to use a VirtualHost for URL you want to use for sharing your work:
<VirtualHost *:80>
ServerName "demo.example.com"
ProxyPass / http://127.0.0.1:1337/
ProxyPassReverse / http://127.0.0.1:1337/
</VirtualHost>
The ServerName Directive tells Apache to listen for client requests for demo.example.com. ProxyPass and ProxyPassReverse configure Apache to forward all requests to the local port 1337.
Apache: Restart to load VHost
$ sudo apachectl restart
Localhost Setup
Make sure you're running a local web application
This should be pretty obvious
It doesn't matter what port it's running, you can configure that when you start the SSH reverse proxy.
Create an SSH Reverse Proxy
To actually enable the connection form your public web server to your localhost, you have to create a reverse proxy using SSH. The reverse proxy will create a connection from your localhost to the remote server (demo.example.com), then have all traffic from a designated remote port forwarded to a local port.
$ ssh -fNR 1337:localhost:80 demo.example.com
-fputs ssh into the background. If you want to be able to easily kill the Proxy later, omit the -f and usectrl + c.-Ndisables all output keeping things clean. If you omit this, you'll see SSH connect to your remote server.-R 1337:localhost:80Tells SSH to create a reverse proxy and that all traffic on the remote port 1337 should be forwarded to localhost's port 80.demo.example.comis the remote server you want SSH to connect to.
You may need to change your localhost port depending on what port your local environment is running on. For example: if you're developing a rails application using the builtin webserver, you might need to use port 3000 instead of port 80.
You should now be able to hit demo.example.com in your browser, and see your application load.
npm jshint – command not found after install
I was getting a new machine up and running and decided to play with zsh. Later that evening I was going to write some JavaScript and wanted to lint my code with the handy jshint tool. So I installed it with NPM:
$ npm install jshint -g
And was then greeted with a command not found error:
$ jshint zsh: correct jslint to slit [nyae]? n zsh: command not found: jslint
Turns out I needed to add the npm bin directory to my PATH, as it didn't get added there automatically since I installed zsh after node/npm.
export PATH="/usr/local/share/npm/bin:${PATH}"
Jenkins & JSHint – Integrating with Checkstyles and Violations
Prerequisite
You need to have some form of jshint installed on your server. Instructions of how to do that are out of scope for this post, and should be able to be easily found on the internet. I used the version that installed with npm.
Jenkins Build Tasks
This is a very simple setup that uses 2 Build > Execute Shell tasks. In the first shell task I created the checkstyle output:
jshint --checkstyle-reporter app/webroot/javascripts/mdc/ > build/logs/checkstyle-jshint.xml || exit 0
In the second, I created the jslint output:
jshint --jslint-reporter app/webroot/javascripts/mdc/ > build/logs/jslint.xml || exit 0

Checkstyle Plugin
Configure the checkstyles plugin to reference the created checkstyle-jshint.xml file:
Violations Plugin
Configure the violations plugin to reference the created checkstyle-jshint.xml and the jslint.xml file:
SVN Remove & Ignore Causing Tree Conflict
We has some files that were being tracked in SVN that we needed to be untracked and ignored, specifically a web.config file, which was being dynamically generated. We removed and ignored the file in a feature branch. We then merged the feature branch into trunk. Then ran svn update on our servers, which caused a tree conflict.
After some googling, the resolution was the following command on the server:
svn resolve --accept working -R .
Thanks to http://www.learnaholic.me/2009/10/04/subversion-resolve-for-tree-conflict/ for the answer.
Filename Cache Busting with CakePHP
This assumes you're using CakePHP 2.x
CakePHP let's you use timestamp based cache busting on your assets by enabling Asset.timestamp in your core.php file. By enabling this and using the builtin HtmlHelper::css and HtmlHelper::script methods, your assets will have a timestamp appended to them as a query string. For example:
/app.js?123456789
However, older squid proxies will not cache anything that has a query string, thus preventing your files from being cached. An alternative solution is to move the timestamp into the filename, and then use htaccess or similiar rewrite rule system to map those files back to the actual files on disk. The end output would be something like the following:
/app.123456789.js
To implement this in CakePHP, you need to override the assetTimestamp in AppHelper.php
Use the following in your .htaccess file. If you're not using Apache, see the html5 boilerplate for nginx or web.config files.
Read more about filename cache busting at the Html5 Boilerplate Wiki entry on cache busting.



