Customizing NGINX

per-domain nginx=1 for Nginx-only processing with Nginx reverse proxy

If you're running with the custombuild options.conf setting webserver=nginx_apache, where apache is behind an nginx proxy, then by default, all domains are listed in both the User nginx.conf and httpd.conf. All connections run through the nginx.conf, and some requests are passed through to apache as needed while static files are processed by nginx.

This feature lets you shut off the proxy part, just for 1 domain, such that the domain is processed 100% by nginx, without using apache at all. PHP is still enabled for the nginx-only domain, which must use php-fpm just like stand-alone nginx.

To enable this for a given domain, add:

nginx=1

to the domain's config in:

/usr/local/directadmin/data/users/FRED/domains/DOMAIN.COM.conf

and issue a rewrite:

/usr/local/directadmin/custombuild/build rewrite_confs

JSON and Relative CMD_* calls

CMD_NGINX_TEMPLATES

View the state of a domain
CMD_ADDITIONAL_DOMAINS?action=view&domain=testdomain%2Ecom&ipp=50&json=yes&redirect=yes

is a dump of the domain.com.conf file, and will include this if enabled:

nginx=1

Also:

HAS_PER_DOMAIN_NGINX=yes

will be shown if this value is allowed to be controlled.

Saving the nginx=1 setting
CMD_DOMAIN
method: POST
action=modify
domain=testdomain.com
form_version=1.1
...
nginx=1

Save along with all other settings (php, cgi, etc), OR you can save just this item:

method: POST
action=modify
domain=testdomain.com
only_affect=nginx
nginx=1

Disabling per-domain Nginx-only processing

To disable per-domain Nginx-only processing, use either:

nginx=0

or simply omit the nginx variable entirely, as it is a checkbox, in which the absence of a value equates to 'off'.

Within the following file:

/usr/local/directadmin/data/skins/enhanced/user/modify_domain.html

the following has been added after php:

|*if HAS_PER_DOMAIN_NGINX="yes"|
        <tr>
            <td>|LANG_NGINX_ONLY|</td>
            <td align=center><input type="checkbox" name="nginx" value="1" |NGINX_CHECKED|></td>
            <td>|LANG_NGINX_ONLY_DESC|</td>
        </tr>
|*endif|

Language tokens have been added as well to the file:

/usr/local/directadmin/data/skins/enhanced/lang/en/user/modify_domain.html

which includes:

LANG_NGINX_ONLY=Nginx-Only
LANG_NGINX_ONLY_DESC=Use only Nginx. Proxy to apache is disabled.

User selectable Nginx templates

This feature allows Users to select from pre-made templates to be inserted into the nginx server entries for common scripts like WordPress. The list of templates can be managed through DA.

This is a JSON-only feature, so Enhanced will not have it, though Evolution will.
It will apply only to domains and not to subdomains (similar to E-Mail accounts). Should you need an nginx template on a subdomain, create it as a full domain instead.

JSON and Relative CMD_* calls

CMD_NGINX_TEMPLATES

View the list of Nginx templates

To view the list of nginx templates, and to view the current locations for the domain:

CMD_NGINX_TEMPLATES
method: GET
domain=domain.com

Output will resemble the following:

{
	"locations": 
	{
		"/wordpress": 
		{
			"nginx_template": "wordpress_with_fastcgi_cache"
		}
	},
	"settings": 
	{
		"domain_nginx": "0",
		"nginx": "1",
		"nginx_proxy": "0"
	},
	"templates": 
	{
		"wordpress_with_fastcgi_cache": 
		{
			"name": "WordPress",
			"path": "/etc/nginx/templates/wordpress_with_fastcgi_cache.conf"
		}
	}
}

The domain_nginx setting will be set to 1 when:

  1. directadmin.conf has:
  • nginx=0
  • nginx_proxy=1

And...

  1. /usr/local/directadmin/data/users/fred/domains/domain.com.conf has:
  • nginx=1

Related: Nginx-only processing for specified domains via the Nginx Reverse Proxy setupopen in new window

If you have both settings[nginx] and settings[domain_nginx] as 0 (but nginx_proyx=1),then you'd need to throw a warning (as no POSTs will be accepted) advising the User to enable the domain nginx=1 via the only_affect=nginx option: https://www.directadmin.com/features.php?id=2953open in new window

Saving a template location setting

To set a given location to a template, use:

CMD_NGINX_TEMPLATES
method: POST
domain=domain.com
action=save
location=/wordpress
nginx_template=wordpress_with_fastcgi_cache
Deleting a template location setting

To clear a list of template locations, use:

CMD_NGINX_TEMPLATES
method: POST
domain=domain.com
action=delete
select1=/wordpress
(select2=/otherpath)

Relative Tokens

The following global token exists:

HAVE_NGINX_TEMPLATES=yes|no

if the feature is available in the current license.

Relative Templates

CustomBuild will manage the templates in /etc/nginx/templates/*.conf.

The format of the template will be a standard nginx code to be inserted into an nginx location block. The first line of the template is a data line, and should contain something like this:

#INFO=name=WordPress

where everything after the #INFO= string is url-encoded, allowing for extra vars in the future. If you do not specify the name, DA will name it the name of the file, less .conf (wordpress_with_fastcgi_cache).

Before the Include to this file, dA will set the $template_location variable to "/wordpress", for example, which can be used in the Included template conf.

        location ~ ^(/wordpress)(/.*)?$
        {
                set $template_location "$1/";
                Include /etc/nginx/templates/wordpress_with_fastcgi_cache.conf;
                include /usr/local/directadmin/data/users/testuser/nginx_php.conf;
        }

DirectAdmin will store the list of locations in:

/usr/local/directadmin/data/users/USERNAME/domains/DOMAIN.COM.locations.json

which is a json file, listing each location. Each location is an array, and the sub-item that applies here would be the template name, eg:

{
        "/wordpress" : {
                "nginx_template" : "wordpress_with_fastcgi_cache"
        }
}

as this leaves other options which could be set for that location in the future.

Nginx locations: new internal structure for tokens

As nginx locations are quite rigid in terms of not allowing conflicts/duplicates, we've hit the point where in order to allow 2 features to use the same location block, internal management is now required.

The following global token will indicate if the feature is available:

HAVE_NGINX_TEMPLATES=yes|no
Relative Templates

The templates exist here:

nginx_server.conf
nginx_server_sub.conf
nginx_server_secure.conf
nginx_server_secure_sub.conf

The old content was as follows:

|*else|
|UNIT_PROXY|
|NGINX_REDIRECTS|
|PROTECTED_DIRECTORIES|
|HOTLINK_PROTECTION|
|EXTRA_LOCATIONS|
|*endif|

And the new content is as follows:

|*else|
|HOTLINK_PROTECTION|
|EXTRA_LOCATIONS|
|LOCATION_BLOCKS|
|*endif|

Using custom global templates with Nginx

This guide will describe how to set up customized global per-User/Domain templates so that your Nginx server configuration can be changed however you need. Note that this guide is a mirror of the similar guide for Apache.

Copy the templates to the custom area:

cd /usr/local/directadmin/data/templates/custom
cp ../nginx_*.conf .

There are many templates for Nginx, so if there are any that you don't need to change, you can delete them from the custom folder and the defaults will be used. Templates in the custom folder override the default templates.

The rest of the changes are very similar to the Apache guide and share essentially the same TOKENS. If you want to see all tokens that you have to work with, you can use the |DUMPTOKENS|open in new window token in the Nginx configuration file, issue a rewrite, and it will add all tokens and their values to your configs. Of course, this won't be syntactically valid, but it will let you see what you can work with.

When you want to issue a rewrite to try your new configs, type:

cd /usr/local/directadmin/custombuild
./build rewrite_confs

How to add a custom module to Nginx

If you want to add any extra modules to Nginx, they'll most likely need to be compiled in. Any module that needs to be compiled in will have a --add-module type flag which will need to be used. To add this flag, run the following:

cd /usr/local/directadmin/custombuild
mkdir -p custom/nginx
cp -fp configure/nginx/configure.nginx custom/nginx/configure.nginx

Add your --add-module line to the end of the custom/nginx/configure.nginx file, and make sure the \ character exists at the end of all lines except the last one. The \ character tells the configure line to loop to the next line, making the configure settings easier to read. Without the \ character to trigger the wrap, the next line becomes a separate command, which is not correct. Once set, type:

./build used_configs

to ensure your custom configure.nginx file is being used. Then type:

./build nginx

How to enable PUT and DELETE methods in Nginx

Use the same guide as is used for Apache as it is applicable to Nginx also.

How to force HTTPS with Nginx

If you're running Nginx, go to Admin Level -> Custom Httpd Config -> example.com and in token |CUSTOM4|, add this code:

|*if SSL_TEMPLATE="0"|
       return 301 https://$host$request_uri;
|*endif|

How to configure a wildcard *.example.com subdomain with Nginx

This guide is an extension of the Apache version and applies to a server running just Nginx or Apache with Nginx as a proxy (nginx_proxy).

  1. Use step 1 in the other guide to setup the DNS in the same manner.

  2. Use the tool Admin Level -> Custom HTTPD Configuration -> domain.com to edit the Nginx configuration and add this code into the top text area:

    server_name *.|DOMAIN|;

In a similar method to that used in the Apache guide, if you want normal control of other subdomains and the full domain, you'll use the zzzz subdomain method:

|*if SUB="zzzz"|
    server_name *.|DOMAIN|;
|*endif|

How to setup webmail.example.com with Nginx

Similar to the Apache version of this guide, Nginx has the ability to set up a webmail.domain.com subdomain using the templates.

In this example, we'll be setting up a subdomain for Roundcube, so adjust the value on the left as desired (eg: roundcube or any other script you have in /var/www/html).

  1. You'll need to setup the server section for the webmail subdomain. This can be accomplished by adding a 2nd server section below the domain's main server section.
cd /usr/local/directadmin/data/templates
cp nginx_server.conf custom
cp nginx_server_secure.conf custom
cd custom
  1. Edit this copied nginx_server.conf, and at the bottom of the template below the existing server section, add this 2nd server section:
server
{
       listen |IP|:|PORT_80|;
       |MULTI_IP|

       server_name webmail.|DOMAIN|;

       root /var/www/html/roundcube;
       index index.php index.html index.htm;
       access_log /var/log/nginx/domains/|DOMAIN|.log;
       access_log /var/log/nginx/domains/|DOMAIN|.bytes bytes;
       error_log /var/log/nginx/domains/|DOMAIN|.error.log;

|*if HAVE_PHP1_FPM="1"|
       # use fastcgi for all php files
       location ~ \.php$
       {
               try_files $uri =404;
               fastcgi_split_path_info ^(.+\.php)(/.+)$;
               include /etc/nginx/fastcgi_params;
               fastcgi_index index.php;
               fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
               include /etc/nginx/nginx_limits.conf;

               if (-f $request_filename)
               {
                       fastcgi_pass unix:/usr/local/php|PHP1_RELEASE|/sockets/webapps.sock;
               }
       }
|*endif|

|*if HAVE_NGINX_PROXY="1"|
       location /
       {
               # access_log off;
               proxy_pass http://127.0.0.1:|PORT_8080|;
               proxy_set_header X-Client-IP      $remote_addr;
               proxy_set_header X-Accel-Internal /nginx_static_files;
               proxy_set_header Host             $host;
               proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
       }
       location /nginx_static_files/
       {
               # access_log  /var/log/nginx/access_log_proxy;
               alias       /var/www/html/roundcube/;
               internal;
       }
|*endif|

       # deny access to apache .htaccess files
       location ~ /\.ht
       {
               deny all;
       }
}
  1. Also modify the secure host nginx_server_secure.conf file. Add:
server
{
       listen |IP|:|PORT_443| ssl|SPACE_HTTP2|;
       |MULTI_IP|

       server_name webmail.|DOMAIN|;

       root /var/www/html/roundcube;
       index index.php index.html index.htm;
       access_log /var/log/nginx/domains/|DOMAIN|.log;
       access_log /var/log/nginx/domains/|DOMAIN|.bytes bytes;
       error_log /var/log/nginx/domains/|DOMAIN|.error.log;

        ssl_certificate |CERT|;
        ssl_certificate_key |KEY|;

|*if HAVE_PHP1_FPM="1"|
       # use fastcgi for all php files
       location ~ \.php$
       {
               try_files $uri =404;
               fastcgi_split_path_info ^(.+\.php)(/.+)$;
               include /etc/nginx/fastcgi_params;
               fastcgi_index index.php;
               fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
               include /etc/nginx/nginx_limits.conf;

               if (-f $request_filename)
               {
                       fastcgi_pass unix:/usr/local/php|PHP1_RELEASE|/sockets/webapps.sock;
               }
       }
|*endif|

|*if HAVE_NGINX_PROXY="1"|
       location /
       {
                proxy_buffering |PROXY_BUFFERING|;
                proxy_pass https://|PROXY_IP|:|PORT_8081|;
                proxy_set_header X-Client-IP      $remote_addr;
                proxy_set_header X-Accel-Internal /nginx_static_files;
                proxy_set_header Host             $host;
                proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
                proxy_hide_header Upgrade;

       }
       location /nginx_static_files/
       {
               # access_log  /var/log/nginx/access_log_proxy;
               alias       /var/www/html/roundcube/;
               internal;
       }
|*endif|

       # deny access to apache .htaccess files
       location ~ /\.ht
       {
               deny all;
       }
}
  1. Save, exit, then rewrite users' nginx.conf files with:
cd /usr/local/directadmin/custombuild
./build rewrite_confs
  1. You'll also need to setup the DNS portion.
cd /usr/local/directadmin/data/templates
cp dns_a.conf custom
cd custom
echo "webmail=|IP|" >> dns_a.conf

This will setup the webmail A record for new DNS zones. For existing DNS zones, you'll have to manually add the webmail A record to point to the domains' IPs.

How to activate web cache. Cache-Control HTTP headers

For a bit of threory, please check the apache section.

Activating per domain

Per domain caching can be activated via DirectAdmin admin page -> Custom HTTPD configuration -> find a domain -> nginx.conf , click CUSTOMIZE and in the CUSTOM3 tab, enter the following:

location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico)$ { expires 30d; add_header Cache-Control "public, no-transform"; }
location ~* \.(jpg|jpeg|gif|png|svg)$ {expires 365d;}
location ~* \.(pdf|css|html|js|swf)$ {expires 2d;}

Activating for all domains

To activate globally for all domains, create a directory:

mkdir -p /usr/local/directadmin/data/templates/custom/
cd /usr/local/directadmin/data/templates/custom/

And create a file called cust_nginx.CUSTOM.post, insert the same code as used above for activating for a single domain, and rewrite configs:

cd /usr/local/directadmin/custombuild
./build rewrite_confs

How to install the Pagespeed Nginx module

Mod_pagespeed is an open-source Apache HTTP Server (or Nginx) module, which automatically applies chosen filters to pages and associated assets such as stylesheets, JavaScript, and HTML files, as well as to images and website cache requirements.

One major advantage of this module is that it does not require modifications to existing content or workflow, meaning that all internal optimizations and changes to files are made on the server side, presenting modified files directly to the user. Each of 40+ filters correspond to one of Google's web performance best practices rules.

Tested with Nginx on CentOS 8:

  1. Follow the official documentation concerning the module installation from https://www.modpagespeed.com/doc/build_ngx_pagespeed_from_sourceopen in new window (see the "Build instructions" section):
NPS_VERSION=1.13.35.2-stable
cd
wget https://github.com/apache/incubator-pagespeed-ngx/archive/v${NPS_VERSION}.zip
unzip v${NPS_VERSION}.zip
nps_dir=$(find . -name "*pagespeed-ngx-${NPS_VERSION}" -type d)
cd "$nps_dir"
NPS_RELEASE_NUMBER=${NPS_VERSION/beta/}
NPS_RELEASE_NUMBER=${NPS_VERSION/stable/}
psol_url=https://dl.google.com/dl/page-speed/psol/${NPS_RELEASE_NUMBER}.tar.gz
[ -e scripts/format_binary_url.sh ] && psol_url=$(scripts/format_binary_url.sh PSOL_BINARY_URL)
wget ${psol_url}
tar -xzvf $(basename ${psol_url})
  1. Customize the Nginx config file to add the external module:
cd /usr/local/directadmin/custombuild/
mkdir -p custom/nginx
cp configure/nginx/configure.nginx custom/nginx/

Open the custom/nginx/configure.nginx file and add the following option before the last string:

"--add-module=/root/incubator-pagespeed-ngx-1.13.35.2-stable" \

And build nginx:

./build nginx

When completed, you should see the "--add-module=/root/incubator-pagespeed-ngx-1.13.35.2-stable" in the output of:

nginx -V
  1. Create the working directory:
mkdir -p /var/cache/ngx_pagespeed

Open the /etc/nginx/nginx-includes.conf file and add the following to enable the module globally:

pagespeed on;
pagespeed FileCachePath /var/cache/ngx_pagespeed;
pagespeed EnableFilters flatten_css_imports;

And restart Nginx:

service nginx restart
  1. Test if it is working with:
curl -Is domain.com | grep X-Page-Speed

You should be able to find text similar to the following in curl's output:

X-Page-Speed: 1.13.35.2-0

Adding security headers to get A+ rating

This guide is similar to the apache guide for adding security headers, but is tailored to the Nginx webserver. You may refer to the mentioned Apache guide for generalized information regarding security headers and online tools for testing them. Here are the instructions for accomplishing the same with Nginx:

  1. Create the /usr/local/directadmin/data/templates/custom/cust_nginx.CUSTOM.post file

  2. Post the code:

    add_header X-Content-Type-Options "nosniff";
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1";
    add_header X-Download-Options "noopen";
    add_header X-Permitted-Cross-Domain-Policies "master-only";
    add_header X-DNS-Prefetch-Control "on";
    add_header Referrer-Policy "no-referrer-when-downgrade";
    add_header Strict-Transport-Security "max-age=31536000";
    add_header Content-Security-Policy "block-all-mixed-content";
    add_header Permissions-Policy 'geolocation=*, midi=(), sync-xhr=(self "https://|DOMAIN|" "https://www.|DOMAIN|"), microphone=(), camera=(), magnetometer=(), gyroscope=(), payment=(), fullscreen=(self "https://|DOMAIN|" "https://www.|DOMAIN|" )';

  1. And rewrite configs:
cd /usr/local/directadmin/custombuild/
./build rewrite_confs

How to enable brotli compression

  1. Extract brotli under any path you prefer. In this case I chose to to extract it in the base of the custombuild directory.
cd /usr/local/directadmin/custombuild
mkdir brotli
git clone https://github.com/google/ngx_brotli.git brotli
cd brotli && git submodule update --init && cd ..
  1. Add a custom add-module directive.
mkdir -p custom/nginx
cp -p ./configure/nginx/configure.nginx custom/nginx/

Add the following somewhere before the last configuration line in custom/nginx/configure.nginx

"--add-module=/usr/local/directadmin/custombuild/brotli" \

make sure to substitute /usr/local/directadmin/custombuild/brotli with whatever path it's been extracted under.

  1. rebuild nginx
./build nginx

This should have compiled nginx with the brotli module.

You can configure brotli by modifying /etc/nginx/nginx-includes.conf. Refer to the documentation found hereopen in new window.

  1. To test that the configuration was successful, add the following to /etc/nginx/nginx-includes.conf:
brotli on;
brotli_static on;

Restart nginx

service nginx restart

curl one of your domains (exampledomain.com in this example).

curl -kL -I -H 'Accept-Encoding: br' http://exampledomain.com 2>&1 | grep -i encoding

If successful, the following output should be returned

Content-Encoding: br
Last Updated: