# Files

> Store uploads in your project and serve them from its address at /_files/, even while your app is asleep, with no extra storage to set up.

Source: https://docs.fortly.com/files

Every project can store files: images, PDFs, videos, downloads. Fortly serves them from your project's own address at `/_files/<path>`. Fortly answers these requests itself and never starts your app to serve a file, so an idle project still serves its images instantly. Files are kept in object storage, not on the machine your app runs on, so they outlive any one server.

The examples on this page use a project created as `my-app` on the account `ada`. Its full name, which API paths use, is `my-app-ada`, and it is served at `https://my-app-ada.fortly.com`.

## How files are served

A file stored at `images/logo.png` is served at:

```text
https://my-app-ada.fortly.com/_files/images/logo.png
```

It is also served at the same path on any [custom domain](https://docs.fortly.com/domains) connected to the project.

- **Public.** Anyone who has the URL can fetch the file, with no token. Do not store anything private here.
- **Served by the platform.** Requests under `/_files/` never reach your app, even when it is running. Do not use `/_files/` for routes of your own.
- **Never run as a page.** An uploaded `.html` file is served as a download, not rendered. Every file is sent with `X-Content-Type-Options: nosniff` and `Content-Security-Policy: default-src 'none'; sandbox`. An upload therefore cannot run script on your project's origin, read its cookies, or call its API as the visitor.
- **Missing files** return `404` with `no such file`.

### Content types

The content type comes from the file's extension. The file's contents and anything sent with the upload are ignored. Some types are shown in the browser, and everything else is sent as a download (`Content-Disposition: attachment`).

| Shown in the browser | Sent as a download |
|---|---|
| Images: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.avif`, `.ico` | `.svg` (an SVG can carry script) |
| Video and audio: `.mp4`, `.webm`, `.mp3` | `.json`, `.csv`, `.zip` |
| Fonts: `.woff`, `.woff2` | `.html`, `.js`, `.css` and any other extension, as `application/octet-stream` |
| `.pdf`, `.txt`, `.log` | |

An `<img src>` pointing at an `.svg` still displays it. Only opening the file directly downloads it.

## Upload a file

In the console, open the project and go to **Storage**. Drop files onto the page or choose **Upload**. Folders are part of a file's path: `images/logo.png` is one file, and the `images` folder exists because that file does. A folder you create in the console is kept only once a file is stored inside it.

With the API, send the file's bytes as the request body. The path after `/files/` is where the file is stored:

```bash
curl -X POST https://api.fortly.com/projects/my-app-ada/files/images/logo.png \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  --data-binary @logo.png
```

A successful upload returns `201` with the stored `path`, its size in `bytes`, and its `url`. Uploading to a path that already holds a file replaces that file, and the new version is served from then on.

> **Warning:**
>
> Use `--data-binary`, not `-d`. curl's `-d` strips line breaks from the data it sends, which corrupts most files.

### Paths

- Up to 512 characters. A leading `/` is ignored, so `/images/logo.png` and `images/logo.png` are the same file.
- `..` is refused with `a file path cannot step outside the project`.
- Line breaks are refused.

> **Tip:**
>
> Use letters, digits, `-`, `_`, `.` and `/` in file paths. Then the stored path and the URL that serves it are the same string, and you never have to think about URL encoding.

### Size

One file can be up to 64 MB. A larger file is refused with `413 Payload Too Large`, and an empty one with `400` and `that file is empty`. All your projects' files count toward your account's storage. See [limits](https://docs.fortly.com/account/limits).

## List files

```bash
curl https://api.fortly.com/projects/my-app-ada/files \
  -H "Authorization: Bearer $FORTLY_TOKEN"
```

```json
{
  "files": [
    {
      "path": "images/logo.png",
      "bytes": 48213,
      "modified": 1760256000,
      "url": "https://my-app-ada.fortly.com/_files/images/logo.png"
    }
  ]
}
```

`modified` is a Unix timestamp in seconds. It is `null` when storage does not report one.

## Rename or move a file

Send the new path as `to`:

```bash
curl -X POST https://api.fortly.com/projects/my-app-ada/rename-file/images/logo.png \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to": "brand/logo.png"}'
```

The file is copied to the new path first and deleted from the old one only after the copy succeeds. Links to the old URL stop working.

## Delete a file

```bash
curl -X DELETE https://api.fortly.com/projects/my-app-ada/files/images/logo.png \
  -H "Authorization: Bearer $FORTLY_TOKEN"
```

The response is `204`, and the file stops being served. In the console, select one or more files under **Storage** and delete them. There is no undo.

Deleting a project deletes all of its files.

## Upload from your app

A running project has no files API of its own. To store files from your code, for example images your users upload, call the management API above from your server.

1. Create an API token under **Settings › API tokens** in the console.
2. Save it as a [secret](https://docs.fortly.com/deploy/environment) on the project under a name such as `FORTLY_API_TOKEN`.
3. Call the files API from your server code:

```js title="upload.js"
// Store a file and return the public URL it is served at.
// `token` is the API token you saved as the FORTLY_API_TOKEN secret,
// read the way your framework exposes secrets on the server.
export async function storeFile(path, bytes, token) {
  const res = await fetch(`https://api.fortly.com/projects/my-app-ada/files/${path}`, {
    method: "POST",
    headers: { Authorization: `Bearer ${token}` },
    body: bytes,
  });
  if (!res.ok) throw new Error(`upload failed (${res.status}): ${await res.text()}`);
  return `https://my-app-ada.fortly.com/_files/${path}`;
}
```

> **Warning:**
>
> An API token can do anything your account can do, including deleting projects. Keep it on the server and never send it to a browser. If your users upload files, check each file's size and type in your own code before you store it, because whatever you store is public at its URL.

For every route, its parameters and responses, see the [files API reference](https://docs.fortly.com/api/files).
