HTTP applications

It is possible to run HTTP APIs and websites on AWS Lambda.

To do that, Bref runs your code on AWS Lambda using PHP-FPM. That means HTTP applications can run on AWS Lambda just like on any other PHP hosting platform.

If you are interested in the details, know that AWS Lambda can react to HTTP requests via API Gateway.

Every code we deploy on AWS Lambda is called a "Function". Do not let this name confuse you: we do deploy HTTP applications in a Lambda Function.

In the Lambda world, an HTTP application is a function that is called by a request and returns a response. Good news: this is exactly what our PHP applications do.

Below is a minimal serverless.yml to deploy HTTP applications. To create it automatically run vendor/bin/bref init.

service: app
    name: aws
    runtime: provided
    - ./vendor/bref/bref
        handler: index.php
            - ${bref:layer.php-73-fpm}
        # This section contains the URL routing configuration of API Gateway
            -   http: 'ANY /'
            -   http: 'ANY /{proxy+}'


The handler is the file that will be invoked when an HTTP request comes in.

It is the same file that is traditionally configured in Apache or Nginx. In Symfony and Laravel this is usually public/index.php but it can be anything.

        handler: public/index.php


The runtime (aka layer) is different than with PHP functions. Instead of php-73 it should be php-73-fpm because we are using PHP-FPM.

            - ${bref:layer.php-73-fpm}

To learn more check out the runtimes documentation.

The /dev/ prefix

API Gateway works with "stages": a stage is an environment (e.g. dev, test, prod).

This is why applications are deployed with URLs ending with the stage name, for example See this StackOverflow question for more information.

If you setup a custom domain for your application this prefix will disappear. If you don't, you need to take this prefix into account in your application routes in your PHP framework.


On AWS Lambda there is no Apache or Nginx. API Gateway acts as the webserver.

To configure HTTP routing we must configure API Gateway using serverless.yml.


The simplest configuration is to catch all incoming requests and send them to PHP. With API Gateway we must define 2 patterns:

  • the / root URL
  • the /* catch-all URL (which does not catch /)

Here is an example of such configuration:

            -   http: 'ANY /'
            -   http: 'ANY /{proxy+}'

Advanced routing

API Gateway provides a routing system that lets us define routes that match specific URLs and HTTP methods. For example:

            -   http: 'POST /articles'
            -   http: 'GET /articles/{id}'

Use {foo} as a placeholder for a parameter and {foo+} as a parameter that matches everything, including sub-folders.


Lambda and API Gateway are only used for executing code. Serving assets via PHP does not make sense as this would be a waste of resources and money.

Deploying a website and serving assets (e.g. CSS, JavaScript, images) is covered in the "Websites" documentation.

In some cases however, you will need to serve images (or other assets) via PHP. One example would be if you served generated images via PHP. In those cases, you need to read the Binary response section below.

Binary responses

By default API Gateway does not support binary HTTP responses like images, PDF, binary files… To achieve this, you need to enable the option for binary responses in serverless.yml:

    # ...
            - '*/*'

This will make API Gateway support binary responses for all responses. Your application can now return binary responses as usual.

However, you must define a Content-Type header on binary responses. If you don't, you may get the following error: Failed encoding Lambda JSON response: Malformed UTF-8 characters, possibly incorrectly encoded. Symfony's helpers or Laravel's helpers will take care of that for you. If you don't use them, here are some examples:

// Vanilla PHP example with a JPEG image response:
header('Content-Type: image/jpeg');
header('Content-Length: ' . filesize($filename));
fpassthru(fopen($filename, 'rb'));

// PSR-7 example:
return $response
    ->withHeader('Content-Type', 'image/jpeg')
    ->withHeader('Content-Length', (string) filesize($filename))
    ->withBody(new Stream($filename));

Cold starts

AWS Lambda automatically destroys Lambda containers that have been unused for 10 to 60 minutes. Warming up a new container can take some time, especially if your package is large or if your Lambda is connected to a VPC. This delay is called cold start.

To mitigate cold starts for HTTP applications, you can periodically send an event to your Lambda including a {warmer: true} key. Bref recognizes this event and immediately responds with a {status: 100} without executing your code.

You can generate automatically such events using AWS CloudWatch (read this article for more details). For example :

            -   http: 'ANY /'
            -   http: 'ANY /{proxy+}'
            - schedule:
                rate: rate(5 minutes)
                    warmer: true