Getting Started with Laravel on Heroku
Last updated May 30, 2024
Table of Contents
This guide will walk you through the configuration and deployment of a Laravel 5 application. For a general introduction to using PHP on Heroku, please refer to Getting Started with PHP on Heroku.
Prerequisites
- PHP (and ideally some Laravel) knowledge.
- A Heroku user account. Signup is free and instant.
- Familiarity with the getting Started with PHP on Heroku guide, with PHP, Composer and the Heroku CLI installed on your computer.
Creating a Laravel application
The application in this tutorial is based on the Laravel Installation guide. It’s worth a read before following the instructions in this article.
Installing a new Laravel project
The composer create-project
command is one of the ways you can bootstrap a new project based on the laravel/laravel standard application skeleton. The command below sets it up in a directory named hello_laravel_heroku
using the latest version of the framework.
After downloading an extensive number of dependencies and running a few hooks, Composer will have set up a fully functional project in the directory you specified, so you can cd
to it.
$ composer create-project laravel/laravel --prefer-dist hello_laravel_heroku
Installing laravel/laravel (v5.1.11)
- Installing laravel/laravel (v5.1.11)
Downloading: 100%
Created project in hello_laravel_heroku
...
...
$ cd hello_laravel_heroku
Initializing a Git repository
It’s now time to initialize a Git repository and commit the current state:
$ git init -b main
Initialized empty Git repository in ~/hello_laravel_heroku/.git/
$ git add .
$ git commit -m "new laravel project"
[main (root-commit) 6ae139d] new laravel project
76 files changed, 5458 insertions(+)
...
Deploying to Heroku
To deploy your application to Heroku, you must first create a Procfile
, which tells Heroku what command to use to launch the web server with the correct settings. After you’ve done that, you’re going to create an application instance on Heroku, configure some Laravel environment variables, and then simply git push
to deploy your code!
Creating a Procfile
By default, Heroku will launch an Apache web server together with PHP to serve applications from the root directory of the project.
However, your application’s document root is the public/
subdirectory, so you need to create a Procfile
that configures the correct document root:
$ echo "web: heroku-php-apache2 public/" > Procfile
$ git add .
$ git commit -m "Procfile for Heroku"
[main 1eb2be6] Procfile for Heroku
1 file changed, 1 insertion(+)
create mode 100644 Procfile
Creating a new application on Heroku
To create a new Heroku application that you can push to, use the heroku create
command:
$ heroku create
Creating mighty-hamlet-1982... done
http://mighty-hamlet-1982.herokuapp.com/ | git@heroku.com:mighty-hamlet-1982.git
Git remote heroku added
As you can see, a random name was automatically chosen for your application (in the example above, it’s mighty-hamlet-1982
).
You are now almost ready to deploy the application. Follow the next section to ensure your Laravel app runs with the correct configuration.
Setting a Laravel encryption key
The application’s encryption key is used by Laravel to encrypt user sessions and other information. Its value will be read from the APP_KEY
environment variable.
As it must comply with the rules of the selected cipher in the configuration, the easiest way to generate a valid key is using the php artisan key:generate --show
command, which will print a key that you can copy and then paste into the next step.
You can simply set environment variables using the heroku config
command, so run a heroku config:set
as the last step before deploying your app for the first time:
$ heroku config:set APP_KEY=…
Setting config vars and restarting mighty-hamlet-1982... done, v3
APP_KEY: ZEqur46KTEG91iWPhKGY42wtwi3rtkx2
Replace …
in the command above with the key you copied from the php artisan key:generate --show
command output.
Instead of manually replacing the …
placeholder in the command above, you can also run
heroku config:set APP_KEY=$(php artisan --no-ansi key:generate --show)
.
Pushing to Heroku
Next, it’s time to deploy to Heroku:
$ git push heroku main
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 379 bytes | 0 bytes/s, done.
Total 4 (delta 3), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Fetching custom git buildpack... done
remote: -----> PHP app detected
remote: -----> Resolved 'composer.lock' requirement for PHP to version 5.6.14.
remote: -----> Installing system packages...
remote: - PHP 5.6.14
remote: - Apache 2.4.10
remote: - Nginx 1.6.0
remote: -----> Installing PHP extensions...
remote: - mbstring (composer.lock; bundled)
remote: - zend-opcache (automatic; bundled)
remote: -----> Installing dependencies...
remote: Composer version 1.0.0-alpha10 2015-04-14 21:18:51
remote: Loading composer repositories with package information
remote: Installing dependencies from lock file
...
remote: - Installing laravel/framework (v5.1.19)
remote: Downloading: 100%
remote:
remote: Generating optimized autoload files
remote: Generating optimized class loader
remote: Compiling common classes
remote: -----> Preparing runtime environment...
remote: -----> Discovering process types
remote: Procfile declares types -> web
remote:
remote: -----> Compressing... done, 74.5MB
remote: -----> Launching... done, v5
remote: https://mighty-hamlet-1982.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/mighty-hamlet-1982.git
1eb2be6..1b70999 main -> main
And that’s it! If you now open your browser, either by manually pointing it to the URL heroku create
gave you, or by using the Heroku CLI to launch a new browser window, the application will respond.
$ heroku open
Opening mighty-hamlet-1982... done
You should see a message in your browser that says “Laravel 5”.
Best Practices
Changing the log destination for production
By default, Laravel will log errors and messages into a directory on disk, which isn’t ideal, because Heroku uses an ephemeral filesystem and treats logs as streams of events across all running dynos.
In order to ensure that errors, should they arise, are visible in heroku logs
, the corresponding configuration in config/logging.php
must be changed so that all relevant channels use the errorlog
driver; for example:
<?php
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single'],
],
'single' => [
'driver' => 'errorlog',
'level' => 'debug',
],
…
Do not forget to add, commit, and, if appropriate, push this change:
$ git add config/logging.php
$ git commit -m "use errorlog"
[main 1b70999] use errorlog
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push heroku main
As you can see in the code snippet above, a modern standard Laravel installation will already honor the LOG_CHANNEL
environment variable for determining the default log destination.
Because errorlog
is also a logging channel in Laravel’s default logging configuration, you can thus use that environment variable, without any code changes, to set the log destination for your app:
$ heroku config:set LOG_CHANNEL=errorlog
Trusting the Load Balancer
Heroku’s HTTP Routing routes each request through a layer of reverse proxies which are, among other things, responsible for load balancing and terminating SSL connections. This means that requests received by a dyno will have the last router’s IP address in the REMOTE_ADDR
environment variable, and the internal request will always be made using the HTTP protocol, even if the original request was made over HTTPS.
Like most common load balancers or reverse proxies, Heroku provides the original request information in X-Forwarded-…
headers (as documented here). Laravel uses components from the Symfony framework for HTTP request handling, and Symfony can easily be configured to trust such headers.
Since Heroku sends all requests to an application through a load balancer first, and that load balancer always sets the headers (making it impossible for a client to forge their values), you can configure Laravel to treat the current remote address (which is the Heroku router) as a trusted proxy.
It is very important to also prevent Laravel from trusting the Forwarded
and X-Forwarded-Host
headers, because Heroku’s router does not set those, but Symfony’s Request component trusts them out of the box once a trusted proxy is set.
Following the Laravel documentation for trusting proxies, your final App\Http\Middleware\TrustProxies
middleware should look like this:
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;
class TrustProxies extends Middleware
{
protected $proxies = '*';
protected $headers = Request:: HEADER_X_FORWARDED_AWS_ELB;
}
With this approach, if you deploy your application to other environments, you must similarly ensure that all traffic arrives through a trusted source, or conditionally only set the trusted proxy when you detect Heroku as the environment your application is running in.
Further reading
- Read the Heroku PHP Support Dev Center article to learn about available versions, extensions, features and behaviors.
- Review the instructions on customizing web server and runtime settings for PHP to learn more about configuring Apache, Nginx and PHP.
- Browse the PHP category on Dev Center for more resources.