Generating your own custom nginx configuration

In my last blog post I have presented you a tool I use to get “The perfect SSL configuration for your server”. This week I want to talk about another tool I use quite frequently to get a good nginx configuration. The tool could once be found at (which still redirects) and is now hosted as the DigitalOcean Community Tools – NGINXConfig.

The basics

The tool presents itself with some basic settings. It’s set to PHP and uses for the domain. This is the first setting to change. With the “Add site” option you can also add mutiple domains which will result in multiple “Per-website” configurations.

The basic configuration options for the technology and hostname

In the Presets you can choose from various other widely used technologies. If you want to run a WordPress site behind nginx, choose the specific preset. It will generate the necessary rewrite rules and some security rules as well.


In this first section you set your hostname, the subdomain you want (or dont want) to use and the server path. You can also specifiy if you use Let’s Encrypt to get your certificates (or set a custom one), setup a PHP server/socket (and a backup) and some other more advanched options.

Global config

In the HTTPS settings group you will find the SSL profiles from the last blog post. It’s usually best to leave them at “Mozilla Intermediate”. When using the configuration tool for a WordPress website make sure to check the “Security | Content-Security-Policy” setting. As WordPress embeds inline CSS and JS it need to be allowed to run those scripts. If you set this policy to strict, your website might not work properly. When you have selected WordPress in the presets, the tool will give you a message on how to set this policy.

Another group of setting you might want to customize are found in the “Performance” tab. The expiration is only set to one week. As WordPress usually handles cache invalidation for assets quite well (be appending the plugin, theme or WordPress version) you can probably use a longer expiration period. But if you host multiple different system behind one nginx, you might want to be more careful here.

Saving your settings

Any time you change some settings, the URL in the browser will change. You can also find this URL in the “Tools” tab in the “Share configuration” field. I would highly recommend that you save or bookmark this URL, but it can also be found in the nginx.conf file of the resulting package.

Applying your configuration

Once you have finished customizing the configuration, follow the instructions in the “Setup” section. You will probably start by downloding the configuration archive and uploading it to your server and extracting it there. If you don’t have a running Let’s Encrypt installation, you can find some more commands on how to get this set up as well.

Testing your configuration

As I’ve also said in my previous blog post, you should never blindly copy/paste a configuration without checking it before you apply it to a running server. The wordpress.conf for example denies access to the xmlrpc.php file. This might be a resonable counter messure against brute force attacks to XML-RPC, but if you use the official WordPress apps, this file must not be blocked. So after applying the configuration, check some important pages on both frontend and backend, if they still work.

Help improving the tool

As this is a community tool, you can report issues or suggest improvements in the projects GitHub repository. I’ve done that myself two times. One issue I have found after updating the configurations was a deny pattern blocking the wp-comments-post.php file, which made commenting on the blog impossible. So if you find something, please help making the tool better for everyone.


Setting up a nginx server is really hard, if you don’t to that as your daily job. I only have to change my configuration so rarely, that I often have to look up basic things (I maintain Apache webservers more often in my day job). Tools like this really helped me a lot getting a first start and finding some good best practices. Another great ressource for such best practices are the configurations from the HTML5 Boilerplate. They don’t only offer example configurations for nginx but also for many other servers. But those are more for advanched users, as they do not offer a configuration tool.

The perfect SSL configuration for your server

It’s 2021 and we are all using SSL for all our website, right? Unfortunately there are still many sites out there not using SSL. But even those who fo use SSL often use insecure settings. Today, I want to give you some tips for tools you can use to optimize the SSL settings – not only for your web servers.

Analyzing the current status

Before we can optimize the settings, we should first get an idea of what we want/have to optimize. My go to tools is the SSL Server Test (Qualys SSL Labs) where you simply paste your host name and run the test. The test result might look like this:

SSL Labs test result

If your result looks something like this, you could stop reading now, as it’s almost the best result you can get. Getting also 100 with “Cipher Strength” is only possible, if the server is using the latest components and if you change various settings deep down in your OS, some of them invole manual re-compilation of components. And reaching 100 points in all four categories might look nice, but you may exclude some people from visitig your site, as you can only offer TLS 1.3, which some older (but not completely outdated) clients do not support.

Optimizing the SSL settings

Some of the checks will test your SSL certificate, but most of them will look on your web server configurations and here you can improve your settings the most.

Get a SSL certificate with a large key size

As for the SSL certificate, make sure that you get one if a key size of 4096. If you use Let’s Encrypt, you can get such a certificate by adding --rsa-key-size 4096 to your certbot call. Similar options are also available for many other Let’s Encrypt clients.

Generating the SSL settings for your server

Many of you might know Apache and nginx, but there are many more web servers (many of them open source) you can use. But also don’t forget you mail server, load balancer, database server, etc. when configuring SSL. And all of them are using different configuration files. Even setting the list of ciphers the server can use may have a different syntax. But forunately there is handy tool to help you, the moz://a SSL Configuration Generator. Here you simply select the “Server Software” you want to configure and the “Mozilla Configuration” which you want to use. For the later, you should probably fo with “Intermediate”:

Mozilla SSL Configuration Generator

You can optionally set version numbers of your server and OpenSSL which might be usefule if your are on a very old hosting that still runs Apache 2.2 or some other old software. In the “Miscellaneous” section you will also find a “HTTP Strict Transport Security” (HSTS) setting. When you set this incorrectly, you might make site using a subdomain unreachable, if they do not already use SSL (which they also really should).

So even though this tool give you some best practices, don’t blindly copy/paste all of the content into your server configurations. While you are changing the settings, you may want to update them in steps and checking the new results with the SSL Labs test again, until you find a configuration that works for you and has the best ratio between high security and device compatibility.


Setting a WordPress Installation to use SSL became really easy with version 5.7, but configuring a server so it’s only using a secure configuration can be hard. Using the right tools even that can be done rather easy.

Exclude first page from Gravity Forms progress bar

When we have to create more complex forms, we often use the Gravity Forms plugins. In one project, we wanted to use it to implement a form with multiple steps, each only having one radio group. Users should see the number of steps and the progress in the form. This is fairly simple, you can just add a “Page” and the other fields to these pages. When you insert the form into a post or page, you will get the following result:

The form with the “Step 1 of 3” progress bar

You can see the issue in this screenshot. The first pages does not have any form fields, but only a “HTML” field with an introduction text. But as it is a page of the form, the progress bar already starts counting.

Starting at page two

In order to fix this, we want to do two things. First we want to hide the progress bar from the first page. But then we would start with “Step 2 of 3” instead of “Step 1 of 2”, so we also have to update the text for the steps on all following pages.

Read more →

Placing the post or page title inside of the content

WordPress 5.8 is only a little more than two weeks away. Probably the biggest new feature is the introduction of Full Site Editing (FSE). This will enable you to edit the whole website with the block editor. But this will only work, if you use a FSE enabled theme. But there are some handy new block you might want to use even with your current theme. In a new project I worked on lately, I was using one of these new blocks (with the Gutenberg plugin installed).

The “Post Title” block

One of the new blocks will allow you to place the title of a blog post into the content. Even though it’s named “Post Title” it will also work with pages, or any other custom post type.

Let’s say you want to place the title in a colum next so some media element, just select this block at the desired position:

Read more →

Find cause for high LOAD with low CPU load at the same time

This week, one server had a LOAD of more than 2500% (around 150 with 6 CPUs), but the CPU load was only around 5%, so this was a sign, that the CPU load was not the cause of the high LOAD on the server:

top - 15:53:42 up 7 days, 10:01,  2 users,  load average: 159,47, 149,89, 160,80
Tasks: 540 total,   1 running, 468 sleeping,   0 stopped,   0 zombie
%Cpu(s):  2,0 us,  2,0 sy,  0,0 ni,  0,0 id, 95,6 wa,  0,0 hi,  0,4 si,  0,0 st
KiB Mem : 12296516 total,   607940 free,  9710388 used,  1978188 buff/cache
KiB Swap: 12578812 total,  7439140 free,  5139672 used.  1752884 avail Mem 

 5564 [email protected]  20   0   99712  36384   5308 D   4,6  0,3   0:00.22 spamassassin
 1539 root      20   0 2394080  55984   7412 S   2,0  0,5   1365:51 fail2ban-server
 4561 root      20   0   33784   6168   3700 S   1,3  0,1   0:02.39 htop
    8 root      20   0       0      0      0 I   0,7  0,0  11:17.44 rcu_sched

Hard disc operations as a possible reason

Not only a high CPU load, but also a high number of IO operations, read and write access to the hard drive, can cause a high LOAD.

Read more →

Fixing two common issues after switching to a multisite

I’ve written a couple of blog posts related to multisite. But two common issues that might cause problem, I have not yet written about.

Missing to redirect the www subdomain

When you convert your WordPress installation into a multisite, you can either use subfolders or subdomains. If you decide to use the subdomain variant, each site will be matched to a subdomain. And if your installation is not using the www subdomain for the main site, this means that also the www subdomain would be considered as a separate site and you will get this error message:

Read more →

Why you should not put theme code in plugins

I regularly maintain about 40 WordPress websites. Updating plugins and themes can cause issue, but updating core usually goes without any issue. Not so this week on a new website I updated for the first time.

After the core update I got the “There has been a critical error on this website” dialog and the backend was broken. Fortunately I created a fresh backup just before the update. But as the website was not hosted on one of our servers and the FTP account was not showing all files, I first had to use the Web-FTP function from the hoster to be able to download the backup file. With a local copy of the website, the debugging session could start.

Customizing theme icons

Let’s take a look at the code that caused the issue. With debugging mode enabled, I was able to find this error message:

PHP Fatal error:  Uncaught Error: Class 'TwentyTwenty_SVG_Icons' not found in ...

This error was caused by the following lines (code simplified for the example):

function add_custom_twentytwenty_icons() {
	TwentyTwenty_SVG_Icons::$ui_icons['hamburger'] = '<svg><!-- ... --></svg>';
add_action( 'after_setup_theme', 'add_custom_twentytwenty_icons' );

A simple code to add a custom icon to the TwentyTwenty “ui” SVG icons group. This has been done in an action after_setup_theme which looks reasonable. But there was something essential missing. The check, if the class does exists. So how could it be done better?

Fixing the issue

There are several ways in how we can fix this specific issue. Let’s take a look at them to find a good solution.

Check for existence of the class

As the error indicated, the code fails, as the class does not exist and therefore the property cannot be extended. So why don’t we just check that first:

function add_custom_twentytwenty_icons() {
	if ( class_exists( 'TwentyTwenty_SVG_Icons' ) ) {
		TwentyTwenty_SVG_Icons::$ui_icons['hamburger'] = '<svg><!-- ... --></svg>';
add_action( 'after_setup_theme', 'add_custom_twentytwenty_icons' );

This would work. Now we are sure that we only change the property of the class, if it exists. But there are some issues with this solution. What if the class name changes? What if the property name changes? In those cases, we either get another fatal error or a warning and broken code at least.

Overwriting the pluggable class

Guess what? This specific class of TwentyTwenty is pluggable. So if you want to change the behavior of the class, you could simply just declare the class in your own code before it is loaded by TwentyTwenty itself.

The only downside with this: if you only want to add an icon, duplicating the whole class might be a bit of an overkill. You would have to make sure that your cloned class keeps in sync with the original class on future updated of TwentyTwenty.

Use a filter!

Yes, you read that correctly, there is a filter to alter the SVG icons available in TwentyTwenty we could just use to add our own icon:

 * Filters Twenty Twenty's array of icons.
 * The dynamic portion of the hook name, `$group`, refers to
 * the name of the group of icons, either "ui" or "social".
 * @since Twenty Twenty 1.5
 * @param array $arr Array of icons.
$arr = apply_filters( "twentytwenty_svg_icons_{$group}", $arr );

In our case, we wanted to add an icon to the “ui” group, so the new function could look as simple as this:

function add_custom_twentytwenty_icons( $icons ) {
	$icons['hamburger'] = '<svg><!-- ... --></svg>';

	return $icons;
add_action( 'twentytwenty_svg_icons_ui', 'add_custom_twentytwenty_icons' );

This is the best solution. The only downside with this is that this callback would be call for every icon that needs to be printed to the page. Maybe this is why the “clever solution” that caused the error was trying to add the icon just once to the class property.

Why was the original solution causing an error?

But my motivation for this blog post was not only to show that specific error and how it could be solved. It’s about why it became an error. The original code was not stored in a child theme, but in a mu-plugin!

The project was using a “vendor-wp-base-theme” as a child theme to TwentyTwenty. Inside of this child theme, not a wohle lot was happening. In addition, there was a mu-plugin “vendor-wp-base-theme-plugin” which used a PHP class adding widgets, enqueuing styles and scripts, adding some ACF filters and using the code from above to add this one SVG icon.

But why is this an issue? Why would the after_setup_theme action fire but the files from the theme would not have been loaded? The issue lies within the wp-settings.php file. This file will fist load all mu-plugin file, then all plugin files and finally all files from “active and valid themes”, so from both the parent and the child theme, before firing the action:

foreach ( wp_get_active_and_valid_themes() as $theme ) {
	if ( file_exists( $theme . '/functions.php' ) ) {
		include $theme . '/functions.php';
unset( $theme );

 * Fires after the theme is loaded.
 * @since 3.0.0
do_action( 'after_setup_theme' );

So how can we still get the issue? Let’s take a look at the wp_get_active_and_valid_themes function and what it’s (not) doing:

function wp_get_active_and_valid_themes() {
	global $pagenow;

	$themes = array();

	if ( wp_installing() && 'wp-activate.php' !== $pagenow ) {
		return $themes;
	// ...

This function is checking, if WordPress is “installing”, which will also be true, if the core needs to upgrade the database after an update using the wp-admin/upgrade.php file. In this case, the function will return the empty themes array, which will therefore not load the functions.php files from both the parent and child theme and will still fire the after_setup_theme action causing the issue.

Conclusion: never do themes stuff in plugins!

There is a good reason why you should put code to customize a theme into a child theme and code to customize a plugin into another (mu-)plugin. This will prevent issues like the one demonstrated in the code from this blog post. Running theme code in plugins (and vice versa) can very easily cause issues like this, which might be hard to debug, especially when they only occur in rare cases (like a core database update).

If you really have to put code into a place where it would typically does not belong to, always check, if the thing you want to modify exists. And if the original code offers hooks, please always use them. They have been added just for those reasons. Even if you think you are clever and can save some function calls by directly manipulating PHP class properties (or similar things), don’t do that. Others will be very thankful that your code will not break their site with a usually uncritical task like a WordPress major core update. 😉

Creating SVG sprites in combination with wordpress/scripts

In one of my advent calendar blog posts in 2016 I wrote (in German) about how to create a SVG sprite with SVG symbols using a Gulp script. With Gutenberg becoming a major part of WordPress I have used the wordpress/scripts package a lot lately. I a recent project I wanted to create such a SVG symbols sprite again, but I didn’t liked to use Gulp additionally with wordpress/scripts, so I searched for an alternative.

SVG Spritemap Webpack Plugin

After some search I’ve found the svg-spritemap-webpack-plugin which looked quite promising. It’s using webpack (which is also included in wordpress/scripts) and can be used like this:

Read more →

Hide the download button for audio and video blocks

Last week I was asked for help on a website project. In this project, the website embeds some audio files into the page. This is a great feature of WordPress making it so easy to embed media files. But some people dislike, that on some browsers it’s do easy to download those files.

The “download” button in Chrome

In the Chrome browsers, it’s simpler than in any other browsers. By default, any audio and video tag would add a “download” button to the options (the three dots, next to the controls). In this particular project, the button should be removed, to make it at least a little harder to download a file and not “encouraging” anyone to do so.

Hiding the button using the “controlList” attribute

Chrome is the only browser with such a button (I know of) and forunately it also has an easy way to disable it: the controlList attribute. This attribute can have different/multiple values and one of them is the nodownload value.

You may now this, that you can simply add this attribute manually to the block using the “Edit as HTML” view. But as soon as you switch back to the “Edit visually” view, the block will be defect and you can only choose to either recover it (which removes the attribute) or convert it to HTML.

Adding for a feature request in Gutenberg

As this issue was probably not new, I searched for existing tickets and found two. The first one was asking to add a toggle to show the download button. This one referred to a second ticket about the general idea of disabling the download button, also by introducing a toggle.

In this second ticket some good arguments were made against such a toggle. As only Chrome has this button, such a toggle would only “work” in Chrome. And it would not even really work. Because even the attribute will not prevent downloads.

If a audio or video file is embedded with an audio or video HTML tag using a file from the media library, it can be downloaded. It will actually be “downloaded” automatically, once the media is played.

Silently removing the button with a plugin

So even though you cannot prevent the download, you might still want to hide the button in this case you can filter the block rendering and add the attribute there. In the simplest form, it will look like this:

function hide_download_buttons_on_embeds_render_block( $block_content, $block ) {
	if ( 'core/audio' === $block['blockName'] ) {
		$block_content = str_replace(
			'<audio ',
			'<audio controlsList="nodownload" ',
	if ( 'core/video' === $block['blockName'] ) {
		$block_content = str_replace(
			'<video ',
			'<video controlsList="nodownload" ',

	return $block_content;
add_filter( 'render_block', 'hide_download_buttons_on_embeds_render_block', 10, 2 );

You might have to write a bit more code, if the HTML tags in your WordPress installation already uses some other attribute values.


While it’s not possible to prevent downloads of audio and video files from the media library in such a way, it might still be something you want to add to your page.

If you do want to allow downloads – let’s say for episodes of a podcast – it’s much better to actively add a download button using the “File” block. This will not only present a consistent button in browser, it will also make it a lot easier, obvious and accessible to download the media file.

As always, the solution from this blog post can be found as a GIST where you can download the solution as a ZIP file and install it as a plugin.

Accessibility is not a feature

The yearly Global Accessibility Awareness Day is still some weeks away – this year it be celebrated on 20 May 2021 – but some recent discussions made be pick this topic for my next blog post.

It’s not just a number, it’s people!

In a recent discussion on twitter regarding a corona contact tracing app, the importance of an accessible app was discussed once more. I can’t tell you how many time I’ve read statements like these:

Read more →