Nginx is a HTTP server software with focus on core web server and proxy features. It is very common because of its resource efficiency and responsiveness under load. Nginx spawns worker processes, each of which can handle thousands of connections. Each of the connections handled by the worker get placed within an event loop where they exist with other connections. Within the loop, events get processed asynchronously, allowing work to be handled in a non-blocking manner. When the connection closes, it gets removed from the loop. This style of connection processing allows Nginx to scale incredibly far with limited resources.
This page explains how to run Grav with Nginx as the HTTP server and PHP-FPM (FastCGI Process Manager) to process PHP scripts, so these packages need to be installed on your server:
nginx
php-fpm
If you are new to Nginx and don't yet have a basic understanding of block directives/context, it is recommended to read the Nginx Beginners's Guide, especially the sections Configuration File’s Structure and Serving Static Content.
It is assumed that your Nginx configuration is located in /etc/nginx/
and your Grav installation is stored in /var/www/grav/
. The structure of the configuration is a http
block that contains general directives relevant for all pages served by Nginx, as well as one or multiple server
blocks for each page, containing site-specific directives. The main server configuration file is nginx.conf
and stores the http
block, while site-specific configurations (server
blocks) are stored in sites-available
and symlinked to sites-enabled
.
The /var/www
directory and all contained files and folders should be owned by $USER:www-data
(or whatever you name the Nginx user/group). The section Troubleshooting/Permissions explains how to setup file and directory permissions for Grav, in this case using a shared group. Basically what you want is 775
for directories and 664
for files in the Grav directory, so Grav is allowed to modify content and upgrade itself. You should add your user to the www-data
group so you can access files that are created by Grav/Nginx.
The following configuration is an improved version of the default /etc/nginx/nginx.conf
file, mainly with improvements from github.com/h5bp/server-configs-nginx. See their repository for explanations on these settings or the Nginx core module and http module documentation to look up specific directives.
It is recommended to use an updated MIME types definition file (mime.types
) from github.com/h5bp/server-configs-nginx. This will make sure that the types are correctly set for gzip compression.
nginx.conf:
user www-data;
worker_processes auto;
worker_rlimit_nofile 8192; # should be bigger than worker_connections
pid /run/nginx.pid;
events {
use epoll;
worker_connections 8000;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30; # longer values are better for each ssl client, but take up a worker connection longer
types_hash_max_size 2048;
server_tokens off;
# maximum file upload size
# update 'upload_max_filesize' & 'post_max_size' in /etc/php/fpm/php.ini accordingly
client_max_body_size 32m;
# client_body_timeout 60s; # increase for very long file uploads
# set default index file (can be overwritten for each site individually)
index index.html;
# load MIME types
include mime.types; # get this file from https://github.com/h5bp/server-configs-nginx
default_type application/octet-stream; # set default MIME type
# logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# turn on gzip compression
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/javascript
text/plain
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy;
# disable content type sniffing for more security
add_header "X-Content-Type-Options" "nosniff";
# force the latest IE version
add_header "X-UA-Compatible" "IE=Edge";
# enable anti-cross-site scripting filter built into IE 8+
add_header "X-XSS-Protection" "1; mode=block";
# include virtual host configs
include sites-enabled/*;
}
Grav gets shipped with a configuration file for your site in the webserver-configs
directory of your Grav installation. You can copy that file into your nginx config directory:
cp /var/www/grav/webserver-configs/nginx.conf /etc/nginx/sites-available/grav-site
Open that file with an editor and replace "example.com" with your domain/IP (or "localhost" if you want to just run it locally), replace the "root" line with "root /var/www/grav/;" and then create a symbolic link of your site-config in sites-enabled
:
ln -s /etc/nginx/sites-available/grav-site /etc/nginx/sites-enabled/grav-site
Finally let Nginx reload its configuration:
nginx -s reload
httpoxy is a set of vulnerabilities that affect application code running in CGI, or CGI-like environments.
(Source: httpoxy.org)
In order to secure your site against this vulnerability you should block the Proxy
header. This can be done by adding a FastCGI parameter to your config. Simply open the file /etc/nginx/fastcgi.conf
and add this line at the end:
fastcgi_param HTTP_PROXY "";
If you want to use an existing SSL certificate to encrypt your website traffic, this section provides the necessary steps to modify your Nginx configuration for that.
First, create a file /etc/nginx/ssl.conf
with the following content and adjust the paths to your certificate and key file. The last section is about OSCP stapling and requires you to provide a chain+root certificate. If you don't want this, you can comment or remove everything below the OCSP Stapling
comment. If your website is SSL only (including subdomains), you can submit your domain for preloading in browsers at https://hstspreload.appspot.com. If that isn't the case, you can remove preload;
from the line that adds the "Strict-Transport-Security" header. Make sure to check if all of these options work for your setup.
ssl.conf:
# set the paths to your cert and key files here
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions
ssl_session_timeout 24h;
# Use a higher keepalive timeout to reduce the need for repeated handshakes
keepalive_timeout 300s; # up from 75 secs default
# submit domain for preloading in browsers at: https://hstspreload.appspot.com
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload;";
# OCSP stapling
# nginx will poll the CA for signed OCSP responses, and send them to clients so clients don't make their own OCSP calls.
# see https://sslmate.com/blog/post/ocsp_stapling_in_apache_and_nginx on how to create the chain+root
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/example.com.chain+root.crt;
resolver 198.51.100.1 198.51.100.2 203.0.113.66 203.0.113.67 valid=60s;
resolver_timeout 2s;
Now change the content of your Grav-specific config /etc/nginx/sites-available/grav-site
to redirect unencrypted HTTP requests to HTTPS, that means to a server
block listening on port 443 and including your ssl.conf
(replace "example.com" with your domain/IP). You can also change this to redirect from the non-www to the www version of your domain.
grav-site:
# redirect http to non-www https
server {
listen [::]:80;
listen 80;
server_name example.com www.example.com;
return 302 https://example.com$request_uri;
}
# redirect www https to non-www https
server {
listen [::]:443 ssl;
listen 443 ssl;
server_name www.example.com;
# add ssl cert & options
include ssl.conf;
return 302 https://example.com$request_uri;
}
# serve website
server {
listen [::]:443 ssl;
listen 443 ssl;
server_name example.com;
# add ssl cert & options
include ssl.conf;
root /var/www/example.com;
index index.html index.php;
# ...
# the rest of this server block (location directives) is identical to the one from the shipped config
}
Finally reload your Nginx configuration:
nginx -s reload
It is also recommended to enable those in production. These additions to the configuration file will handle them. 'expires' defines the expiration time for the cache, 30 days in this case. Please see the full documentation about http headers for nginx here http://nginx.org/en/docs/http/ngx_http_headers_module.html.
location ~* ^/forms-basic-captcha-image.jpg$ {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
expires 30d;
add_header Vary Accept-Encoding;
log_not_found off;
}
location ~* ^.+\.(?:css|cur|js|jpe?g|gif|htc|ico|png|html|xml|otf|ttf|eot|woff|woff2|svg)$ {
access_log off;
expires 30d;
add_header Cache-Control public;
## No need to bleed constant updates. Send the all shebang in one
## fell swoop.
tcp_nodelay off;
## Set the OS file cache.
open_file_cache max=3000 inactive=120s;
open_file_cache_valid 45s;
open_file_cache_min_uses 2;
open_file_cache_errors off;
}
Found errors? Think you can improve this documentation? Simply click the Edit link at the top of the page, and then the icon on Github to make your changes.
Powered by Grav + with by Trilby Media.