Skip to the content
Documentation

Virtual host setup

No ratings yet

How to serve an Exponential 6 installation. The document root is always the installation root, the directory that holds index.php. Pick one of the options below; the setup wizard needs one of them during the install, exp:install and the kickstarter do not.

  Exponential Velocity Apache or nginx with PHP-FPM
Other web server needed no yes
HTTPS built in: your certificate, Let's Encrypt or any ACME CA, self-signed fallback the web server's, with a certificate from your own tool
Role recommended, from development to production the classic setup, shared hosting

Exponential Velocity (recommended)

Velocity is the application server of Exponential, installed with it by Composer. It loads the application once, keeps it in persistent workers, has its own response cache and serves HTTP and HTTPS itself. The shipped engine is php (PHP's built-in server, for development); set qbix for every real site:

php bin/php/console exp:velocity config set --apply ServerSettings Engine qbix
php bin/php/console exp:velocity start
php bin/php/console exp:velocity status

The qbix engine serves plain HTTP on port 8088 and HTTPS on port 8080, bound to 127.0.0.1. To serve the network on the standard ports (ports below 1024 need root; the workers still run as the installation's user):

php bin/php/console exp:velocity config set --apply ServerSettings Host 0.0.0.0
php bin/php/console exp:velocity config set --apply ServerSettings Port 80
php bin/php/console exp:velocity config set --apply ServerSettings HTTPSPort 443
php bin/php/console exp:velocity restart

HTTPS with a certificate you have (one PEM file may hold both):

php bin/php/console exp:velocity config set --apply HTTPSSettings Certificate /etc/ssl/example.com/fullchain.pem
php bin/php/console exp:velocity config set --apply HTTPSSettings Key /etc/ssl/example.com/privkey.pem
php bin/php/console exp:velocity config set --apply HTTPSSettings Enabled true
php bin/php/console exp:velocity restart
php bin/php/console exp:velocity ssl show

config set writes settings/override/velocity.ini.append.php; the shipped defaults stay in settings/velocity.ini. Without --apply it only shows the change. Velocity's own configuration follows Debian's /etc/apache2 layout under /etc/vc (vc.conf, ports.conf, envvars, sites-available and sites-enabled); exp:velocity layout shows every file it uses.

Day to day: exp:velocity deploy after a PHP, INI or class change (autoloads, caches, PHP-FPM reload and Velocity restart in the right order; --dry-run shows the steps), exp:velocity graceful for a restart without dropped requests, and exp:velocity cache clear --apply for the response cache. Workers keep the application in memory, so clearing caches alone does not reach a code change. A systemd unit to start Velocity at boot is in the Velocity example.

Apache with PHP-FPM

Copy the shipped rewrite rules into place, then add a virtual host:

./console exp:setup:htaccess            # shows what would be copied
./console exp:setup:htaccess --apply    # copies .htaccess_root to .htaccess

mod_rewrite, mod_proxy and mod_proxy_fcgi must be loaded, and mod_ssl for HTTPS. A minimal virtual host and the faster variant without .htaccess are on Virtual host example; what the rules do is on htaccess example. Check with apachectl configtest and reload Apache.

.htaccess slows down every request. When AllowOverride is anything but None, Apache reads and evaluates .htaccess files on every request, in every directory of the path. For production, put the rules into the virtual host itself and set AllowOverride None: the example HTTPS virtual host carries them.

nginx with PHP-FPM

nginx does not read .htaccess; its server block carries the same rules. This one passes nginx -t (nginx 1.28); replace example.com, /var/www/example.com, the PHP-FPM socket and the certificate paths, then run nginx -t and systemctl reload nginx:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name example.com www.example.com;
    ssl_certificate     /etc/ssl/example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/example.com/privkey.pem;

    root /var/www/example.com;
    index index.php;
    client_max_body_size 64m;

    # No dot file or dot directory; .well-known stays reachable
    location ~ (^|/)\.(?!well-known(/|$)) { return 404; }
    # No script below the root runs because its path was asked for
    location ~ ^/.+/[^/]+\.(php[0-9]?|phtml|phar)$ { return 404; }

    # Front controllers
    location ^~ /api/ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index_rest.php;
        fastcgi_param SCRIPT_NAME /index_rest.php;
        fastcgi_pass unix:/run/php-fpm/www.sock;
    }
    location ~ ^/([^/]+/)?content/treemenu {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index_treemenu.php;
        fastcgi_param SCRIPT_NAME /index_treemenu.php;
        fastcgi_pass unix:/run/php-fpm/www.sock;
    }

    # Files served as they are
    location ~ ^/var/([^/]+/)?storage/images(-versioned)?/  { try_files $uri =404; }
    location ~ ^/var/([^/]+/)?storage/original/image/.+\.(png|jpe?g|gif|webp|svg)$ { try_files $uri =404; }
    location ~ ^/var/([^/]+/)?cache/(texttoimage|public)/   { try_files $uri =404; }
    location ~ ^/design/[^/]+/(stylesheets|images|javascript|fonts)/ { try_files $uri =404; }
    location ~ ^/share/icons/                               { try_files $uri =404; }
    location ~ ^/extension/[^/]+/design/[^/]+/(stylesheets|flash|images|lib|javascripts?|fonts|vendor|media)/ { try_files $uri =404; }
    location = /favicon.ico { try_files $uri =404; }
    location = /robots.txt  { try_files $uri =404; }

    # Everything else goes to index.php
    location / {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
        fastcgi_param SCRIPT_NAME /index.php;
        fastcgi_pass unix:/run/php-fpm/www.sock;
    }
}

This is the core of the rules. The complete nginx example adds the security headers, the sandbox for uploaded SVG images, the PDF and package files, the ACME challenge and the SOAP server.

Other servers and control panels

The same rules for LiteSpeed, Caddy, IIS and FrankenPHP, and an overview of all of them: Web server examples.

Behind a reverse proxy or load balancer

Forwarded headers (X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host) are believed only from the addresses in site.ini [HTTPHeaderSettings] TrustedProxies[], by default 127.0.0.1 and ::1. Add a proxy on another machine in settings/override/site.ini.append.php:

[HTTPHeaderSettings]
TrustedProxies[]=10.0.0.0/8

The complete chapter, with FrankenPHP, permissions and HTTPS in depth: Serving the site.

In this section

Contributors

Comments (2 historical)

Written by readers of the earlier documentation sites, kept as they were.

  1. Mistake in last rewrite rule

    Mahmoud Abu-Wardeh ·

    When I tried the rewrite rules above, Apache (2.0.54) complained with "mod_rewrite: maximum number of internal redirects reached", resulting in a 500 error.
    The solution is that the last rewrite rule should read:
    RewriteRule .* index.php # no leading slash
    See http://www.sitepoint.com/print/mod_rewrite-no-endless-loops for an explanation.
    By the way, this VirtualHost setup is quite differet to those in the .htaccess_root file that shops with ez Publish 3.7.2 - would you recommend a selective merge of the two or to stick to this example?

    1. RE: Mistake in last rewrite rule

      André R. ·

      Not correct, I suspect you are talking about .htaccess.
      These pages does not cover .htaccess, but vhost settings.

Comments

Add a comment

Comments are read by a moderator before they appear.