---
title: "Optimizing the Web Server for DreamFactory | DreamFactory Docs"
source: "https://docs.dreamfactory.com/getting-started/optimizing-dreamfactory/web-server"
canonical_url: "https://docs.dreamfactory.com/getting-started/optimizing-dreamfactory/web-server"
converted_at: "2026-09-03T23:24:41.903Z"
format: "markdown"
converted_by: "html-to-md-ai"
---
DreamFactory is usually served by NGINX in front of PHP-FPM, and that is the configuration this page covers. The installer writes a working virtual host for you. What follows is what each part does and when you should change it.

## The Shipped Virtual Host[​](#the-shipped-virtual-host)

The essentials of the configuration DreamFactory installs:

```
upstream php_handler {    server unix:/var/run/php/php8.5-fpm.sock;}server {    listen 80;    server_name your.host.name;    root "/opt/dreamfactory/public";    index index.html index.htm index.php;    charset utf-8;    client_max_body_size 100m;    location / {        try_files $uri $uri/ /index.php?$query_string;    }    location ~ \.php$ {        try_files $uri /index.php?$query_string;        fastcgi_split_path_info ^(.+\.php)(/.+)$;        fastcgi_pass php_handler;        fastcgi_index index.php;        include fastcgi_params;        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;        fastcgi_connect_timeout 60;        fastcgi_send_timeout 180;        fastcgi_read_timeout 180;    }    location ~ /\.ht {        deny all;    }}
```

Two things are load-bearing. The document root is `public`, never the application directory itself, which keeps `.env` and the source tree outside the web root. And the `try_files` fallback routes every unmatched path into `index.php`, which is how Laravel's router sees the request at all.

## Upload Limits[​](#upload-limits)

`client_max_body_size 100m` caps the request body NGINX will accept. Exceed it and the client gets a `413`, from NGINX, before PHP is ever involved.

This value must be kept in step with PHP. Raising uploads means changing all three:

- NGINX `client_max_body_size`

- PHP `upload_max_filesize`

- PHP `post_max_size`

The effective limit is the smallest of the three, which is why raising one and forgetting the others produces a limit nobody can find in the config they just edited. See [PHP and Laravel](/getting-started/optimizing-dreamfactory/php-and-laravel).

## FastCGI Timeouts[​](#fastcgi-timeouts)

```
fastcgi_connect_timeout 60;fastcgi_send_timeout 180;fastcgi_read_timeout 180;
```

`fastcgi_read_timeout` is the one you will meet. It is how long NGINX waits for PHP to respond, and when it expires the client gets a `504 Gateway Timeout` even though PHP is still working on the request.

Raise it when you have legitimately slow operations: a scripted service calling a slow third-party API, a large export, a bulk import. Raise PHP's `max_execution_time` to match, or you have simply moved the failure from NGINX to PHP.

Before raising either, check whether the request is slow for a fixable reason. A `504` on a database endpoint is usually a missing index, not a timeout that needs to be longer. See [Optimizing Database APIs](/getting-started/optimizing-dreamfactory/database-apis).

## Never Cache the Admin UI Entry Point[​](#never-cache-the-admin-ui-entry-point)

```
location = /dreamfactory/dist/index.html {    add_header Cache-Control "no-store" always;}
```

Keep this rule. The admin interface is an Angular application with content-hashed chunk filenames. Those hashed assets are safe to cache aggressively and should be. `index.html` is the file that points at them, so a cached copy pins users to an old build indefinitely, and the symptom is bizarre: a browser that keeps loading a version of the UI that no longer exists on the server, fixed only by a hard refresh that most users will never think to try.

If you put a CDN or reverse proxy in front of DreamFactory, carry the same rule forward there.

## Compression[​](#compression)

JSON API responses compress extremely well, often to a fraction of their original size, and enabling compression is one of the cheapest wins available on a slow or metered network.

```
gzip on;gzip_types application/json application/javascript text/css text/plain;gzip_min_length 1024;
```

Do not compress everything indiscriminately. Very small responses get larger, not smaller, once framing overhead is counted, which is what `gzip_min_length` guards against. Already-compressed payloads such as images or archives gain nothing and cost CPU.

## Logging[​](#logging)

The shipped configuration sets `access_log off` and logs only errors. That is a deliberate trade: on a busy API gateway, access logging every request is a meaningful amount of disk I/O for data most operators never read.

Turn it on when you are diagnosing something:

```
access_log /var/log/nginx/dreamfactory-access.log;
```

Then turn it back off, or rotate it aggressively. An API that never sleeps fills a disk faster than most people expect, and a full disk takes the instance down.

## TLS[​](#tls)

Terminate TLS at NGINX or at a load balancer in front of it. Certificate setup is covered in [CORS and SSL](/system-settings/config/cors-and-ssl).

When something else terminates TLS and forwards over plain HTTP, DreamFactory only sees the HTTP leg. Laravel then builds redirects — including the root-route redirect to the admin UI — with an `http://` `Location` header, and generated API documentation uses the wrong scheme.

Two mechanisms exist. Use both when a proxy terminates TLS.

### Pass the original scheme through to PHP[​](#pass-the-original-scheme-through-to-php)

In your NGINX site:

```
# in the http { } blockmap $http_x_forwarded_proto $fcgi_https { default off; https on; }# in server { } -> location ~ \.php$ { }fastcgi_param HTTPS $fcgi_https;
```

```
sudo nginx -t && sudo systemctl reload nginx
```

When NGINX itself terminates TLS, `fastcgi_param HTTPS on;` is enough. When a load balancer in front of NGINX terminates TLS, map `X-Forwarded-Proto` as above so PHP sees the external scheme.

### Force HTTPS in the application[​](#force-https-in-the-application)

Set this in `.env`:

```
FORCE_HTTPS=true
```

Then run `php artisan config:clear` and restart PHP-FPM. This is the application-level switch: it calls `URL::forceScheme('https')` and is what actually changes generated redirects. Do **not** run `config:cache` after setting it — `FORCE_HTTPS` is read from the environment at boot, not from cached config, so caching config ignores the setting and redirects go back to `http://`.

`APP_URL` is the external URL of the install. OAuth discovery and similar config-driven URLs read it. It does **not** change request-built redirects.

DreamFactory does not ship `app/Http/Middleware/TrustProxies.php`. Do not add one.

MCP OAuth discovery is a special case that reads `APP_URL` directly; see [Deploying the MCP Server](/AI/mcp-server-deployment#running-behind-a-reverse-proxy).

## Related Reading[​](#related-reading)

- [PHP and Laravel](/getting-started/optimizing-dreamfactory/php-and-laravel)

- [Optimizing Database APIs](/getting-started/optimizing-dreamfactory/database-apis)

- [CORS and SSL](/system-settings/config/cors-and-ssl)

- [Deploying the MCP Server](/AI/mcp-server-deployment#running-behind-a-reverse-proxy) — MCP OAuth discovery and `APP_URL`