> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Web

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

export const DynamicCodeBlock = ({language = 'yaml', filename, icon, lines, wrap, expandable, highlight, focus, children}) => {
  const STORAGE_KEY = 'upsun_versions_cache';
  const COMPOSABLE_STORAGE_KEY = 'upsun_composable_cache';
  const CACHE_TTL = 5 * 60 * 1000;
  const API_URL = 'https://meta.upsun.com/images';
  const COMPOSABLE_API_URL = 'https://meta.upsun.com/composable';
  const DEBUG_PREFIX = '[DynamicCodeBlock cache]';
  const [versionData, setVersionData] = useState(null);
  const [versionError, setVersionError] = useState(false);
  const [composableData, setComposableData] = useState(null);
  const [composableError, setComposableError] = useState(false);
  useEffect(() => {
    const fetchData = async () => {
      let cachedData = null;
      let cachedEtag = null;
      if (typeof localStorage !== 'undefined') {
        try {
          const cached = localStorage.getItem(STORAGE_KEY);
          if (cached) {
            const parsed = JSON.parse(cached);
            cachedData = parsed?.data || null;
            cachedEtag = parsed?.etag || null;
            if (cachedData && Date.now() - parsed.timestamp < CACHE_TTL) {
              return cachedData;
            }
          }
        } catch (err) {
          console.error('Failed to load from cache:', err);
        }
      }
      const requestHeaders = cachedEtag ? {
        'If-None-Match': cachedEtag
      } : {};
      console.debug(`${DEBUG_PREFIX} revalidating`, {
        storageKey: STORAGE_KEY,
        hasCachedData: Boolean(cachedData),
        hasCachedEtag: Boolean(cachedEtag)
      });
      const response = await fetch(API_URL, {
        headers: requestHeaders
      });
      if (response.status === 304 && cachedData) {
        console.debug(`${DEBUG_PREFIX} revalidated (304)`, {
          storageKey: STORAGE_KEY
        });
        if (typeof localStorage !== 'undefined') {
          try {
            const etag = response.headers.get('etag') || cachedEtag;
            localStorage.setItem(STORAGE_KEY, JSON.stringify({
              data: cachedData,
              etag,
              timestamp: Date.now()
            }));
          } catch (err) {
            console.error('Failed to refresh cache metadata:', err);
          }
        }
        return cachedData;
      }
      if (!response.ok) throw new Error(`API request failed: ${response.statusText}`);
      const data = await response.json();
      const etag = response.headers.get('etag');
      console.debug(`${DEBUG_PREFIX} refreshed (200)`, {
        storageKey: STORAGE_KEY,
        etag
      });
      if (typeof localStorage !== 'undefined') {
        try {
          localStorage.setItem(STORAGE_KEY, JSON.stringify({
            data,
            etag,
            timestamp: Date.now()
          }));
        } catch (err) {
          console.error('Failed to cache data:', err);
        }
      }
      return data;
    };
    fetchData().then(data => setVersionData(data)).catch(err => console.error('Failed to fetch version data:', err));
  }, []);
  const findHighestVersion = versionsMap => {
    if (!versionsMap || Object.keys(versionsMap).length === 0) return null;
    const entries = Object.entries(versionsMap);
    const active = entries.filter(([, v]) => v.upsun && v.upsun.status === 'supported' || v.upsun && v.upsun.status === 'deprecated');
    const candidates = active.length > 0 ? active : entries;
    let [highestName] = candidates[0];
    for (let i = 1; i < candidates.length; i++) {
      const [currentName] = candidates[i];
      const cp = currentName.split('.').map(Number);
      const hp = highestName.split('.').map(Number);
      for (let j = 0; j < Math.max(cp.length, hp.length); j++) {
        if ((cp[j] || 0) > (hp[j] || 0)) {
          highestName = currentName;
          break;
        } else if ((cp[j] || 0) < (hp[j] || 0)) {
          break;
        }
      }
    }
    return highestName;
  };
  const getVersion = (lang, requestedVersion = 'latest') => {
    if (lang === 'composable') {
      if (!composableData || !composableData.versions || Object.keys(composableData.versions).length === 0) return null;
      if (requestedVersion && requestedVersion !== 'latest') {
        return (requestedVersion in composableData.versions) ? requestedVersion : null;
      }
      return findHighestVersion(composableData.versions);
    }
    if (!versionData) return null;
    const imageData = versionData[lang];
    if (!imageData || !imageData.versions || Object.keys(imageData.versions).length === 0) {
      return null;
    }
    if (requestedVersion && requestedVersion !== 'latest') {
      return (requestedVersion in imageData.versions) ? requestedVersion : null;
    }
    return findHighestVersion(imageData.versions);
  };
  let code = typeof children === 'string' ? children : String(children || '');
  const codeLines = code.split('\n');
  while (codeLines.length > 0 && codeLines[0].trim() === '') codeLines.shift();
  while (codeLines.length > 0 && codeLines[codeLines.length - 1].trim() === '') codeLines.pop();
  if (codeLines.length > 0) {
    const indents = codeLines.filter(line => line.trim().length > 0).map(line => line.match(/^[ \t]*/)[0].length);
    const minIndent = Math.min(...indents);
    code = codeLines.map(line => line.slice(minIndent)).join('\n');
  }
  code = code.replace(/\{\{version:(.*?)\}\}/g, (match, params) => {
    const parts = params.split(':');
    const lang = parts[0];
    const ver = parts[1] || 'latest';
    const isComposable = lang === 'composable';
    const hasError = isComposable ? composableError : versionError;
    const dataReady = isComposable ? composableData !== null : versionData !== null;
    if (hasError) return '(unavailable)';
    if (dataReady) {
      const resolvedVersion = getVersion(lang, ver);
      return resolvedVersion || match;
    }
    return '...';
  });
  const codeBlockProps = {
    language,
    ...filename && ({
      filename
    }),
    ...icon && ({
      icon
    }),
    ...lines !== undefined && ({
      lines
    }),
    ...wrap !== undefined && ({
      wrap
    }),
    ...expandable !== undefined && ({
      expandable
    }),
    ...highlight && ({
      highlight
    }),
    ...focus && ({
      focus
    })
  };
  return <CodeBlock {...codeBlockProps}>{code}</CodeBlock>;
};

Optional in [single-runtime](/docs/configure-apps/app-reference/single-runtime-image#primary-application-properties) and [composable](/docs/configure-apps/app-reference/composable-image#primary-application-properties) 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`](/docs/configure-apps/app-reference/single-runtime-image#type), which defines the base container used to run the application.

| Name        | Type                                       | Required                      | Description                                          |
| ----------- | ------------------------------------------ | ----------------------------- | ---------------------------------------------------- |
| `commands`  | A [web commands dictionary](#web-commands) | See [`start`](#start-command) | The command to launch your app.                      |
| `upstream`  | An [upstream dictionary](#upstream)        |                               | How the front server connects to your app.           |
| `locations` | A [locations dictionary](#locations)       |                               | How the app container responds to incoming requests. |

See some [examples of how to configure what's served](/docs/configure-apps/web).

### Web commands

| Name                                | Type     | Required                                                  | Description                                                                                                                                                     |
| ----------------------------------- | -------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`pre_start`](#pre_start-command)   | `string` |                                                           | Command run just prior to `start`, which can be useful when you need to run *per-instance* actions. Non-blocking.                                               |
| [`start`](#start-command)           | `string` | Typically, except for PHP; see [`start`](#start-command). | The command to launch your app. If it terminates, it's restarted immediately.                                                                                   |
| [`post_start`](#post_start-command) | `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

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.<br />
To avoid issues, make sure any critical initialization in `pre_start` can complete quickly or is safe to run concurrently with `deploy`.

### `start` command

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](https://github.com/platformsh-examples/platformsh-example-reactphp)
or [Amp](https://github.com/platformsh-examples/platformsh-example-amphp).
See [Alternate start commands](/docs/languages/php#alternate-start-commands) in the PHP topic.

<Note>
  Do not run a `start` process in the background by using `&` syntax.
  The Upsun supervisor interprets that syntax as the command terminating and starts another copy, creating a loop that continues until the container crashes.
  Run the command as usual and allow the Upsun supervisor to manage it.
</Note>

### `post_start` command

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:

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.

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: "python:{{version:python:latest}}"
                  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`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: "composable:{{version:composable:latest}}"
                  source:
                    root: "/"
                  stack: 
                    runtimes: [ "python@{{version:python:latest}}" ]
                  web:
                    commands:
                      start: 'uwsgi --ini conf/server.ini'
                      post_start: |
                      date
                      curl -sS --retry 20 --retry-delay 1 --retry-connrefused localhost -o /dev/null`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

### `upstream`

<Tabs>
  <Tab title="Single-runtime image">
    | 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](/docs/configure-apps/app-reference/single-runtime-image#type) 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`](/docs/configure-apps/app-reference/single-runtime-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:

    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                myapp:
                  type: 'python:{{version:python:latest}}'
                  source:
                    root: "/"
                  web:
                    upstream:
                      socket_family: tcp
                      protocol: http`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    | 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](/docs/configure-apps/app-reference/composable-image#working-with-multiple-runtimes-primary-runtime) 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](/docs/configure-apps/app-reference/composable-image#working-with-multiple-runtimes-primary-runtime).                                         |

    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:

    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                myapp:
                  type: "composable:{{version:composable:latest}}"
                  source:
                    root: "/"
                  stack: 
                    runtimes: [ "python@{{version:python:latest}}" ]
                  web:
                    upstream:
                      socket_family: tcp
                      protocol: http`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

#### Where to listen

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](/docs/development/variables/use-variables#use-provided-variables)               |
| `unix`          | The Unix socket file specified by the [`SOCKET` environment variable](/docs/development/variables/use-variables#use-provided-variables) |

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`

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:

<Tabs>
  <Tab title="Single-runtime image">
    | Name                | Type                                                 | Default   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
    | ------------------- | ---------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `root`              | `string`                                             |           | The directory to serve static assets for this location relative to the [app's root directory](/docs/configure-apps/app-reference/single-runtime-image#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`. <br /> <br /> 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](/docs/configure-apps/web/custom-headers)). Responses from the app aren't affected.                                                                                                                                                                                                                                                                                                                     |
    | `request_buffering` | A [request buffering dictionary](#request-buffering) | See below | Handling for chunked requests.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
    | `rules`             | A [rules dictionary](#rules)                         |           | Specific overrides for specific locations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
  </Tab>

  <Tab title="Composable image">
    | 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`](/docs/configure-apps/image-properties/source)). 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`. <br /> <br /> 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](/docs/configure-apps/web/custom-headers)). Responses from the app aren't affected.                                                                                                                                                                                                                                                                                                                     |
    | `request_buffering` | A [request buffering dictionary](#request-buffering) | See below | Handling for chunked requests.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
    | `rules`             | A [rules dictionary](#rules)                         |           | Specific overrides for specific locations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
  </Tab>
</Tabs>

#### Rules

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](#locations)
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.

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                myapp:
                  type: 'python:{{version:python:latest}}'
                  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`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                myapp:
                  type: "composable:{{version:composable:latest}}"
                  source:
                    root: "/"
                  stack: 
                    runtimes: [ "python@{{version:python:latest}}" ]
                  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`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

#### Request buffering

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:

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                myapp:
                  type: 'python:{{version:python:latest}}'
                  source:
                    root: "/"
                  web:
                    locations:
                      '/':
                        passthru: true
                        request_buffering:
                          enabled: true
                          max_request_size: 250m`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                myapp:
                  type: "composable:{{version:composable:latest}}"
                  source:
                    root: "/"
                  stack: 
                    runtimes: [ "python@{{version:python:latest}}" ]
                  web:
                    locations:
                      '/':
                        passthru: true
                        request_buffering:
                          enabled: true
                          max_request_size: 250m`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>
