Platform.sh is now Upsun. Click here to learn more
Upsun Fixed User Documentation

web

Try Upsun for 15 days
After that, enjoy the same game-changing Upsun features for less with the First Project Incentive!¹ A monthly $19 perk!
Activate your 15-day trial
¹Terms and conditions apply. Only for Flexible Resource projects.

A web instance that defines how the web application is served.

Optional in single-runtime and composable images.

Use the web key to configure the web server running in front of your app.

For single-runtime images, default values might vary based on the image type, which defines the base container used to run the application.

Name Type Required Description
commands A web commands dictionary Typically, except for PHP. See start. The command to launch your app.
upstream An upstream dictionary How the front server connects to your app.
locations A locations dictionary How the app container responds to incoming requests.

See some examples of how to configure what’s served.

commands (web commands) Anchor to this heading

Name Type Required Description
pre_start string Command run just prior to start, which can be useful when you need to run per-instance actions. Non-blocking.
start string Typically, except for PHP; see start. The command to launch your app. If it terminates, it’s restarted immediately.
post_start string Command runs after the start command and before adding the container to the router. Can be used to ensure app is active before routing traffic to it.

pre_start command Anchor to this heading

The pre_start command is not blocking, which means the deploy hook may start running before the pre_start command finishes. This can lead to unexpected behavior if pre_start performs setup tasks that deploy depends on.
To avoid issues, make sure any critical initialization in pre_start can complete quickly or is safe to run concurrently with deploy.

start command Anchor to this heading

On all containers other than PHP, it’s a best practice to include a start command. This command runs every time your app is restarted, regardless of whether new code is deployed.

On PHP containers, start is optional and defaults to starting PHP-FPM (/usr/bin/start-php-app). You can set it explicitly on a PHP container to run a dedicated process, such as React PHP or Amp. See Alternate start commands in the PHP topic.

post_start command Anchor to this heading

You can use the post_start command to ensure your app is fully active before traffic is routed to it. This command can perform checks or wait until your application starts listening on the expected port.

For example, if your framework needs several seconds to initialize (for example, to build caches or establish database connections), post_start can help coordinate the handover to ensure that the app receives traffic only after it is initialized.

Example: Anchor to this heading

This example contains two web commands:

  • A start command that starts the application every time, whether or not new code is deployed.
  • A post_start command that repeatedly checks whether a service on localhost is responding.
.platform.app.yaml
name: APP_NAME
type: "python:3.14"
source:
  root: "/"
web:
  commands:
    start: 'uwsgi --ini conf/server.ini'
    post_start: |
      date
      curl -sS --retry 20 --retry-delay 1 --retry-connrefused localhost -o /dev/null      
.platform/applications.yaml
APP_NAME:
  type: "composable:26.05"
  source:
    root: "/"
  stack: 
    runtimes: [ "python3.14" ]
  web:
    commands:
      start: 'uwsgi --ini conf/server.ini'
      post_start: |
        date
        curl -sS --retry 20 --retry-delay 1 --retry-connrefused localhost -o /dev/null        

upstream Anchor to this heading

Name Type Required Description Default
socket_family tcp or unix Whether your app listens on a Unix or TCP socket. Defaults to tcp for all image types except PHP; for PHP image types the default is unix.
protocol http or fastcgi Whether your app receives incoming requests over HTTP or FastCGI. Default varies based on image type.

For PHP, the defaults are configured for PHP-FPM and shouldn’t need adjustment. For all other containers, the default for protocol is http.

The following example is the default on non-PHP containers:

.platform.app.yaml
name: myapp
type: 'python:3.14'
source:
  root: "/"
web:
  upstream:
    socket_family: tcp
    protocol: http
Name Type Required Description Default
socket_family tcp or unix Whether your app listens on a Unix or TCP socket. Defaults to tcp for all primary runtimes except PHP; for PHP the default is unix.
protocol http or fastcgi Whether your app receives incoming requests over HTTP or FastCGI. Default varies based on the primary runtimes.

For PHP, the defaults are configured for PHP-FPM and shouldn’t need adjustment. For all other containers, the default for protocol is http.

The following example is the default on non-PHP containers:

.platform/applications.yaml
myapp:
  type: "composable:26.05"
  source:
    root: "/"
  stack: 
    runtimes: [ "python@3.14" ]
  web:
    upstream:
      socket_family: tcp
      protocol: http

Where to listen Anchor to this heading

Where to listen depends on your setting for web.upstream.socket_family (defaults to tcp).

socket_family Where to listen
tcp The port specified by the PORT environment variable
unix The Unix socket file specified by the SOCKET environment variable

If your application isn’t listening at the same place that the runtime is sending requests, you see 502 Bad Gateway errors when you try to connect to your website.

locations Anchor to this heading

Each key in the locations dictionary is a path on your site with a leading /. For example.com, a / matches example.com/ and /admin matches example.com/admin. When multiple keys match an incoming request, the most-specific applies.

The following table presents possible properties for each location:

Name Type Default Description
root string The directory to serve static assets for this location relative to the app’s root directory. Must be an actual directory inside the root directory.
passthru boolean or string false Whether to forward disallowed and missing resources from this location to the app. A string is a path with a leading / to the controller, such as /index.php.

If your app is in PHP, when setting passthru to true, you might want to set scripts to false for enhanced security. This prevents PHP scripts from being executed from the specified location. You might also want to set allow to false so that not only PHP scripts can’t be executed, but their source code also can’t be delivered.
index string array or null Files to consider when serving a request for a directory. When set, requires access to the files through the allow or rules keys.
expires string -1 How long static assets are cached. The default means no caching. Setting it to a value enables the Cache-Control and Expires headers. Times can be suffixed with ms = milliseconds, s = seconds, m = minutes, h = hours, d = days, w = weeks, M = months/30d, or y = years/365d. If a Cache-Control appears on the headers configuration, expires, if set, will be ignored. Thus, make sure to set the Cache-Control’s max-age value when specifying a header.
allow boolean true Whether to allow serving files which don’t match a rule.
scripts boolean Whether to allow scripts to run. Doesn’t apply to paths specified in passthru. Meaningful only on PHP containers.
headers A headers dictionary Any additional headers to apply to static assets, mapping header names to values (see Set custom headers on static content). Responses from the app aren’t affected.
request_buffering A request buffering dictionary See below Handling for chunked requests.
dynamic_compression boolean false Whether to compress dynamic responses from your app.
rules A rules dictionary Specific overrides for specific locations.
Name Type Default Description
root string The directory to serve static assets for this location relative to the app’s root directory (see source.root). Must be an actual directory inside the root directory.
passthru boolean or string false Whether to forward disallowed and missing resources from this location to the app. A string is a path with a leading / to the controller, such as /index.php.

If your app is in PHP, when setting passthru to true, you might want to set scripts to false for enhanced security. This prevents PHP scripts from being executed from the specified location. You might also want to set allow to false so that not only PHP scripts can’t be executed, but their source code also can’t be delivered.
index string array or null Files to consider when serving a request for a directory. When set, requires access to the files through the allow or rules keys.
expires string -1 How long static assets are cached. The default means no caching. Setting it to a value enables the Cache-Control and Expires headers. Times can be suffixed with ms = milliseconds, s = seconds, m = minutes, h = hours, d = days, w = weeks, M = months/30d, or y = years/365d.
allow boolean true Whether to allow serving files which don’t match a rule.
scripts boolean Whether to allow scripts to run. Doesn’t apply to paths specified in passthru. Meaningful only on PHP containers.
headers A headers dictionary Any additional headers to apply to static assets, mapping header names to values (see Set custom headers on static content). Responses from the app aren’t affected.
request_buffering A request buffering dictionary See below Handling for chunked requests.
dynamic_compression boolean false Whether to compress dynamic responses from your app.
rules A rules dictionary Specific overrides for specific locations.

Rules Anchor to this heading

The rules dictionary can override most other keys according to a regular expression. The key of each item is a regular expression to match paths exactly. If an incoming request matches the rule, it’s handled by the properties under the rule, overriding any conflicting rules from the rest of the locations dictionary.

Under rules, you can set all the other possible locations properties except root, index, rules and request_buffering.

In the following example, the allow key disallows requests for static files anywhere in the site. This is overridden by a rule that explicitly allows common image file formats.

.platform.app.yaml
name: myapp
type: 'python:3.14'
source:
  root: "/"
web:
  locations:
    '/':
      # Handle dynamic requests
      root: 'public'
      passthru: '/index.php'
      # Disallow static files
      allow: false
      rules:
        # Allow common image files only.
        '\.(jpe?g|png|gif|svgz?|css|js|map|ico|bmp|eot|woff2?|otf|ttf)$':
          allow: true
.platform/applications.yaml
myapp:
  type: "composable:26.05"
  source:
    root: "/"
  stack: 
    runtimes: [ "python@3.14" ]
  web:
    locations:
      '/':
        # Handle dynamic requests
        root: 'public'
        passthru: '/index.php'
        # Disallow static files
        allow: false
        rules:
          # Allow common image files only.
          '\.(jpe?g|png|gif|svgz?|css|js|map|ico|bmp|eot|woff2?|otf|ttf)$':
            allow: true

Dynamic response compression Anchor to this heading

Static files are compressed automatically, but responses generated by your app aren’t. Set dynamic_compression to true on a location to compress them:

.platform.app.yaml
name: myapp
type: 'php:8.5'  
source:
  root: "/"   
web:
  locations:
    '/':
      root: 'public'
      passthru: '/index.php'
      dynamic_compression: true
.platform/applications.yaml
myapp:  
  type: "composable:26.05"  
  source:
    root: "/"
  stack:
    runtimes: [ "php@8.5" ]
  web:
    locations:
      '/':
        root: 'public'
        passthru: '/index.php'
        dynamic_compression: true

The algorithm is chosen based on the request’s Accept-Encoding header, with Brotli preferred and gzip as the fallback. Requests without that header get an uncompressed response. Compressed responses carry a Vary: Accept-Encoding header. Responses that already have a Content-Encoding header are left alone, so an app that compresses its own output isn’t compressed twice.

Compression starts at 256 bytes, on a best-effort basis: the web server can only check the size of responses that declare a Content-Length, which means chunked responses are compressed whatever their size.

To keep some paths uncompressed, turn the setting on for the location and off in a rule:

.platform.app.yaml
name: myapp
type: 'php:8.5'
source:
  root: "/"
web:
  locations:
    '/':
      root: 'public'
      passthru: '/index.php'
      dynamic_compression: true
      rules:
        # Leave the authenticated area uncompressed.
        '^/account/':
          dynamic_compression: false
.platform/applications.yaml
myapp:
  type: "composable:26.05"
  source:
    root: "/"
  stack:
    runtimes: [ "php@8.5" ]
  web:
    locations:
      '/':
        root: 'public'
        passthru: '/index.php'
        dynamic_compression: true
        rules:
          # Leave the authenticated area uncompressed.
          '^/account/':
            dynamic_compression: false

Enable compression on the location and opt out per rule, not the other way around. A rule that turns compression on inside a location where it’s off doesn’t survive the passthru rewrite, which leaves the traffic uncompressed on a typical PHP app. A rule that turns compression off applies to both direct hits and requests served through passthru.

Apps serving large volumes of public, cacheable HTML from the origin get the most out of the setting, especially behind a content delivery network (CDN) that can’t compress what the origin sends it.

Request buffering Anchor to this heading

Request buffering is enabled by default to handle chunked requests as most app servers don’t support them. The following table shows the keys in the request_buffering dictionary:

Name Type Required Default Description
enabled boolean Yes true Whether request buffering is enabled.
max_request_size string 250m The maximum size to allow in one request.

The default configuration would look like this:

.platform.app.yaml
name:  myapp
type: 'python:3.14'
source:
  root: "/"
web:
  locations:
    '/':
      passthru: true
      request_buffering:
        enabled: true
        max_request_size: 250m
.platform/applications.yaml
myapp:
  type: "composable:26.05"
  source:
    root: "/"
  stack: 
    runtimes: [ "python@3.14" ]
  web:
    locations:
      '/':
        passthru: true
        request_buffering:
          enabled: true
          max_request_size: 250m