Web Server
QUIQQER supports Apache, Nginx and FrankenPHP (based on Caddy). Choose your server below for routing configuration and optional media delivery.
| Web server | PHP integration |
|---|---|
| Apache | mod_php or PHP-FPM |
| Nginx | PHP-FPM |
| FrankenPHP / Caddy | Embedded PHP |
The Caddy configuration generator targets FrankenPHP. For standalone Caddy with PHP-FPM, configure routing and PHP integration in your own Caddyfile.
Keep local changes in the custom files described for each server. Regenerate the generated routing files after changing the corresponding QUIQQER settings.
Apache
Routing
Generate the Apache .htaccess file from the installation root:
./console htaccessSee Console for the general command calling pattern.
The generated file includes custom rules from:
etc/htaccess.custom.phpPut local Apache customizations into that custom file instead of editing the generated .htaccess directly. Regenerate .htaccess after changing the custom file.
See Configuration for the role of etc/ files.
The generator creates a backup of an existing .htaccess below var/backup/ before writing the new file. Keep custom rules in etc/htaccess.custom.php; the generated root .htaccess can be replaced during updates or regeneration.
PHP-FPM
QUIQQER's generated Apache rules route virtual URLs to index.php and map special paths such as /bin, /lib, and /admin to the Core package directories.
When Apache is used with PHP-FPM or another proxy/FastCGI setup, make sure:
- rewrite rules are processed before the QUIQQER front controller receives the request
- PHP receives the original request URI and query string
- static package assets below
/packages/,/bin/, and generated media paths stay readable - the web server user and PHP-FPM user can write runtime directories that need writes
If a static path is handled by QUIQQER instead of the web server, add an early exception in etc/htaccess.custom.php and regenerate .htaccess.
Static Directories
When a static directory should be served before QUIQQER handles the request, add the exception near the top of etc/htaccess.custom.php.
Example for a static documentation directory under /docs:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^docs/?$ /docs/index.html [END]
RewriteCond %{DOCUMENT_ROOT}/docs/$1 -f
RewriteRule ^docs/(.*)$ - [END]
RewriteCond %{DOCUMENT_ROOT}/docs/$1 -d
RewriteRule ^docs/(.*)$ - [END]
RewriteCond %{DOCUMENT_ROOT}/docs/$1/index.html -f
RewriteRule ^docs/(.*)/?$ /docs/$1/index.html [END]
RewriteCond %{DOCUMENT_ROOT}/docs/$1.html -f
RewriteRule ^docs/(.*)$ /docs/$1.html [END]
</IfModule>This lets Apache serve existing files, directories, directory index pages, and extensionless static HTML pages under /docs before the QUIQQER front controller handles the request.
Media Delivery
Optional, from QUIQQER 2.34. Read the shared media delivery requirements before configuring X-Sendfile.
Install and load mod_xsendfile, then add the following to the virtual host configuration. XSendFilePath belongs in the server/virtual host configuration, not in the generated .htaccess:
XSendFile On
# For module builds supporting URL decoding, including Debian's 0.12+git build:
XSendFileUnescape Off
XSendFilePath /srv/quiqqer/media
XSendFilePath /srv/quiqqer/var/media
# Apache with mod_php:
SetEnv QUIQQER_SENDFILE_TYPE X-SendfileWith PHP-FPM via mod_proxy_fcgi, replace the SetEnv line with:
ProxyFCGISetEnvIf "true" QUIQQER_SENDFILE_TYPE "X-Sendfile"Symfony passes the resolved filesystem path without URL encoding. Therefore, module builds with URL decoding enabled need XSendFileUnescape Off, including for filenames containing a literal percent sign. Omit that directive only for older module versions without URL decoding support, such as the original 0.12 release.
Keep existing rules denying direct access to protected originals. XSendFilePath permits delegated delivery; it does not block ordinary URL access. Run apachectl configtest before reloading Apache.
Nginx
Routing
QUIQQER also provides a console tool for NGINX configuration generation:
./console quiqqer:nginxUse the generated output as the basis for the web server configuration and keep local server-specific changes in the server configuration managed by operations.
The generator writes the main example configuration to:
etc/nginx/nginx.example.confIt also creates include files below:
etc/nginx/conf.d/Use these include files for local server-specific changes such as PHP-FPM, redirect, SSL, whitelist, and server configuration. Replace the generated example PHP-FPM socket path with the real PHP-FPM socket or upstream used by the server.
The generated NGINX routing uses virtual path checks. Missing real files are rewritten to QUIQQER's front controller, while known static paths and admin paths are handled separately.
Media Delivery
Optional, from QUIQQER 2.34. Read the shared media delivery requirements before configuring X-Accel-Redirect.
Add these parameters inside the existing FastCGI locations that serve image.php and the QUIQQER front controller, after their existing fastcgi_params or fastcgi.conf include:
fastcgi_param QUIQQER_SENDFILE_TYPE X-Accel-Redirect;
fastcgi_param QUIQQER_ACCEL_MAPPING "/srv/quiqqer/media/=/__quiqqer_media/,/srv/quiqqer/var/media/=/__quiqqer_var_media/";Add the corresponding locations at server level:
location ^~ /__quiqqer_media/ {
internal;
alias /srv/quiqqer/media/;
}
location ^~ /__quiqqer_var_media/ {
internal;
alias /srv/quiqqer/var/media/;
}internal prevents direct requests from bypassing the PHP access check. Keep the installation's existing protections for original media URLs as well. Do not disable X-Accel-Redirect processing with fastcgi_ignore_headers, or override private/no-store cache policies in these locations.
Keep these additions in the server configuration managed by operations, rather than editing generated example files. Run nginx -t before reloading NGINX.
FrankenPHP / Caddy
QUIQQER provides FrankenPHP configuration support from QUIQQER 2.23. Optional web server media delivery requires QUIQQER 2.34; see the media delivery setup for the required FrankenPHP and Caddy versions.
Routing
Generate the FrankenPHP routing configuration from the installation root:
./console quiqqer:frankenphpThe generator writes:
etc/webserver/frankenphp/Caddyfile.includeImport this file inside your site's Caddyfile block. Replace the domain and installation path with your own values:
http://example.com, https://example.com {
import /srv/quiqqer/etc/webserver/frankenphp/Caddyfile.include
}Keep domain names, TLS, logging and server-wide settings in your own Caddyfile. The generated include supplies the installation's document root and routing for the frontend, administration interface and package assets.
Put local routing changes in etc/webserver/frankenphp/conf.d/. The generator creates pre.caddy, php.caddy, redirects.caddy, whitelist.caddy, optimizations.caddy and post.caddy if they do not exist, and preserves existing custom files. PHP handler options belong in php.caddy.
Do not edit Caddyfile.include directly. The generator backs up an existing file below var/backup/ before replacing it.
Media Delivery
This is optional. Read the shared media delivery requirements before configuring X-Accel-Redirect.
For this configuration, use FrankenPHP 1.4.0 or later with Caddy 2.9.1 or later. FrankenPHP 1.4.0 includes Caddy 2.9.1, which supports the response interception used here. These server requirements apply in addition to QUIQQER 2.34 or later.
See the FrankenPHP 1.4.0 release and its Caddy dependency.
Integrate the following fragment into the existing QUIQQER site's route, preserving its routing and private-file protections:
route {
# Reject direct requests before PHP handles the request.
respond /__quiqqer_media/* 404
intercept {
@media header X-Accel-Redirect *
handle_response @media {
root * /srv/quiqqer/media
rewrite * {resp.header.X-Accel-Redirect}
uri strip_prefix /__quiqqer_media
header -X-Accel-Redirect
file_server
}
}
php_server {
env QUIQQER_SENDFILE_TYPE X-Accel-Redirect
env QUIQQER_ACCEL_MAPPING /srv/quiqqer/media/=/__quiqqer_media/
}
}The original GET/HEAD method is retained. Files outside this mapping, such as previews in a separate VAR_DIR, continue streaming through PHP. To delegate those as well, add matching internal routes and mappings with their respective filesystem roots and direct-access protection.
For standalone Caddy with PHP-FPM, use the same interception route and place the env parameters inside the existing php_fastcgi block instead of php_server. Validate the complete configuration with frankenphp validate --config /etc/frankenphp/Caddyfile or caddy validate before reloading.
Media Delivery
Available from QUIQQER 2.34.
QUIQQER can check access to a media file in PHP and delegate its delivery to the web server. This avoids passing the file content through PHP and is particularly useful for large files or many concurrent downloads.
No QUIQQER backend setting is required. Enable the feature through the web server configuration in the Apache, Nginx or FrankenPHP / Caddy section. Without that configuration, QUIQQER continues streaming files through PHP.
This applies to image.php and media delivery through QUI\Rewrite::sendFileWithRange(), including backend previews using that method. Other package download endpoints and downloads using QUI\Utils\System\File::downloadHeader() are not covered. SVG files continue through PHP so that their content is sanitized before delivery.
Server Parameters
The web server supplies these internal CGI/environment parameters to PHP:
| Parameter | Value |
|---|---|
QUIQQER_SENDFILE_TYPE | Exactly X-Sendfile for Apache or X-Accel-Redirect for NGINX/Caddy. |
QUIQQER_ACCEL_MAPPING | For X-Accel-Redirect: comma-separated absolute-directory/=internal-uri/ pairs. |
Both sides of each mapping must end with /. The filesystem prefix must match the resolved absolute path visible to PHP. The URI prefix must match the web server's internal file-serving route. The first valid matching pair is used. Do not use commas or equals signs inside mapping paths.
Adapt the examples to the actual media directories and VAR_DIR of the installation. Map only the media directories needed for delivery. The web server must be able to read those files; container deployments need corresponding shared mounts.
Set these parameters directly in the server configuration. Do not derive them from HTTP headers, query parameters or other client input. QUIQQER ignores client-supplied X-Sendfile-Type, X-Accel-Mapping and HTTP_QUIQQER_* values for this feature.
Verify Media Delivery
Test through the public web server using a non-SVG media URL handled by PHP:
- An authorized GET returns the complete file and the expected content type, disposition and cache policy.
- HEAD returns file metadata without a body.
- A request with
Range: bytes=0-9returns status 206 and the first ten bytes. - Unauthorized requests and direct requests to internal media URIs cannot read protected files.
- SVG responses still contain sanitized content.
- Removing the internal server parameters restores PHP streaming, including range support.
- Supplying fake Sendfile HTTP headers does not change the delivery behavior.
Missing or unknown server parameters, and invalid or unmatched acceleration mappings, retain PHP streaming. However, if a server advertises support but its file-serving configuration is broken, PHP cannot retry after handing over the response. Check the server error log, mappings and file permissions.
The server consumes the offload response header. Its absence in the browser alone does not prove delegation; use upstream response inspection or server diagnostics in a test environment when checking the handoff.
See also Symfony's file responses, NGINX internal locations, Apache mod_xsendfile and FrankenPHP file delivery.
Validation
After changing routing rules:
- regenerate the generated server file when Apache
.htaccessis used - reload the web server when the server configuration changed
- test the public homepage
- test the administration interface
- test static directories and assets
- check the web server error log
Troubleshooting
| Symptom | Check |
|---|---|
| Static files return a QUIQQER page | Add an early Apache/NGINX exception for the static path before the front-controller rewrite. |
| Extensionless static docs return 404 | Add a rewrite from the extensionless path to the generated .html file. |
| Admin or frontend returns 403 after web server changes | Check generated deny rules, custom rules, document root, and whether the request is rewritten to the expected target. |
| NGINX returns 502/504 | Check the PHP-FPM socket/upstream path in etc/nginx/conf.d/ and PHP-FPM availability. |
| CLI generation works but web requests fail | Compare CLI user, PHP-FPM user, file ownership, and web server include paths. |
