Dwight Watson's blog

Laravel on Heroku - with Nginx & gzip

This blog post was originally published a little while ago. Please consider that it may no longer be relevant or even accurate.

When setting up a PHP app on Heroku the default configuration is to use Apache. It's simple to get going and customise through the .htaccess file. However out of the box this falls short of what you probably want in a production site.

First you're going to want to switch to using Nginx - as it's generally faster than Apache. Nginx still has first-party support from Heroku, so it's easy to do. Second, you'll want to enable gzip (which is not enabled by default for either Apache or Nginx on Heroku) as that will reduce the time your dyno spends serving requests.

Switching to Nginx

To switch to Nginx we'll need to do two things; first update the Procfile to tell Heroku to use a different server and second provide a configuration for it that works with Laravel. For reference, take a look at the Nginx configuration provided by Laravel's documentation - we need to adjust it a tad to be compatible with Heroku. We'll effectively be merging it up with the default Nginx configuration file that Heroku uses.

In your Procfile update the web: line to use heroku-php-nginx instead of Apache. As usual you'll provide public/ as the public directory but we also provide a custom configuration file - nginx.conf - which will instruct Nginx how to work with Laravel.

web: vendor/bin/heroku-php-nginx -C nginx.conf public/

Next we'll create the nginx.conf file in the root of the app (but you could pop it wherever you want). This adjusts the default Laravel configuration to work with Heroku. Not a lot has changed, you can easily reference both to see the differences.

add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";

index index.php index.html index.htm;

charset utf-8;

location / {
try_files $uri $uri/ /index.php?$query_string;
}

location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }

error_page 404 /index.php;

location ~ /\.(?!well-known).* {
deny all;
}

There's only a one major difference from Laravel's example - we don't use the listen, server_name or root options as that is all handled upstream by Heroku.

Easy enough - once you ship these changes you'll be running Laravel on Heroku with Nginx, be sure to check the performance of your app before and after the change to get a feel for the improvement.

Enabling nginx

For whatever reason, gzip is disabled by default for Heroku's PHP buildpack. It's unfortunate as gzip can provide a performance improvement as your dyno will spend less time serving request.

Luckily, the HTML5 boilerplate provides example optimised server configuration files. Taking a look at the default setup we can see the gzip configuration. Let's apply this to our configuration (stripping the comments).

add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";

index index.php index.html index.htm;

charset utf-8;

gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/plain
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy;

location / {
try_files $uri $uri/ /index.php?$query_string;
}

location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }

error_page 404 /index.php;

location ~ /\.(?!well-known).* {
deny all;
}

With this configuration deployed your Laravel app will now be served through Nginx with everything gzipped. Run another speed test and you should see another speed bump.

I've tried to keep this configuration fairly conventional and unopinionated - using the suggested configuration from Laravel and doing the bare minimum to make it compatible on Heroku and then adding the HTML5 boilerplate's gzip options.

A blog about Laravel & Rails by Dwight Watson;

Picture of Dwight Watson

Follow me on Twitter, or GitHub.