Azure AppService PHP 8.1 & NGINX With WordPress

As of 28th November 2022 PHP 7.4 will no longer be supported and as such users must migrate onto the latest supported version PHP 8.1.

NOTE: Windows based Azure app services do not support PHP 8.1 and as such will no longer be supporting PHP after this date.

So, in the various environments I manage they include a lot of PHP based app services, specifically a lot of WordPress, luckily all Linux based but also all running the soon to be deprecated PHP 7.4. I must upgrade! Simple right?

We know that WordPress is compatible with PHP 8.1, so whats the issue? Just change the drop down to PHP 8.1 and click save? This is where things start to get interesting…

As part of the PHP 8.1 implementation within Azure app services the previously used Apache web server has been replaced for NGINX. A significant and config breaking change.

To give a brief background of the differences between Apache & NGINX – Apache uses directory specific .htaccess files to manage configuration such as rewrite rules whereas NGINX uses a server level configuration file to manage config for all sites.

WordPress is heavily reliant on .htaccess files with rewrite rules for permalinks to function correctly. So how do we replace the current working Apache .htaccess living in the WordPress directory and incorporate it into a new, server level, NGINX config?

Well, the best way seems to be using a custom startup shell script via the app service startup command parameter that copies in a new customized NGINX config file and restarts the NGINX service… let me show you how…

By default the NGINX config on Azure app services lives under /etc/nginx/sites-available in a file called default. As can be seen below –

A pretty basic default config can then be found inside –

Lets take this default config and add the required WordPress configuration based off NGINXs own recommendations for WordPress – https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/#abridged-basic-setup

This gives us the following file –

NOTE: The file shown below is for single WordPress instances, not multisite. For multisite instances you should reference the NGINX documentation linked above and see the section for multisite configuration.

# ***************************************
# WordPress Azure AppService NGINX Config
# ***************************************

# Based off the default Azure AppService NGINX config file this
# file includes the additional required configuration for WordPress.

# See the following link for WordPress NGINX config details inc multisite:
# https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/#abridged-basic-setup

# Mike Hosker - mikehosker.net

upstream php {
	server unix:/tmp/php-cgi.socket;
	server 127.0.0.1:9000;
}

server {
	#proxy_cache cache;
	#proxy_cache_valid 200 1s;
	listen 8080;
	listen [::]:8080;
	root /home/site/wwwroot;
	index  index.php index.html index.htm;
	server_name  example.com www.example.com; 
	port_in_redirect off;

	# Custom to allow large file uploads
	
	client_max_body_size 256M;

	# redirect server error pages to the static page /50x.html
	#
	error_page   500 502 503 504  /50x.html;
	location = /50x.html {
		root   /html/;
	}

	# Disable .git directory
	location ~ /\.git {
		deny all;
		access_log off;
		log_not_found off;
	}

	# Add locations of phpmyadmin here.
	location ~ [^/]\.php(/|$) {
		fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
		fastcgi_pass 127.0.0.1:9000;
		include fastcgi_params;
		fastcgi_param HTTP_PROXY "";
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		fastcgi_param PATH_INFO $fastcgi_path_info;
		fastcgi_param QUERY_STRING $query_string;
		fastcgi_intercept_errors on;
		fastcgi_connect_timeout         300; 
		fastcgi_send_timeout           3600; 
		fastcgi_read_timeout           3600;
		fastcgi_buffer_size 128k;
		fastcgi_buffers 4 256k;
		fastcgi_busy_buffers_size 256k;
		fastcgi_temp_file_write_size 256k;
	}

	location = /favicon.ico {
			log_not_found off;
			access_log off;
	}

	location = /robots.txt {
			allow all;
			log_not_found off;
			access_log off;
	}

	location / {
			# This is cool because no php is touched for static content.
			# include the "?$args" part so non-default permalinks doesn't break when using query string
			try_files $uri $uri/ /index.php?$args;
	}

	location ~ \.php$ {
			#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
			include fastcgi_params;
			fastcgi_intercept_errors on;
			fastcgi_pass php;
			#The following parameter can be also included in fastcgi_params file
			fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
	}

	location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
			expires max;
			log_not_found off;
	}

}

Now we have a config file that should work with WordPress we need to get it copied on to the server and replace the default file that currently exists.

NOTE: In the above NGINX config file the custom client_max_body_size parameter is set to 256M to match the PHP.ini config as described in this post to allow large file uploads.

In order to do this we first need to create some directories and files on the app services persistent storage under /home.

This can be done via FTP by creating a directory under the root called “startup” and a sub directory within called “nginx-conf“, you can then copy the above WordPress NGINX config into a file called “nginx.conf” within the nginx-conf sub directory.

NOTE: If you edit / save the above config file on a Windows machine you may experience problems with hidden characters, therefore I recommend using Notepad ++ and the EOL Conversion functionality within when saving.

You should also create a further file named “startup.sh” within the startup directory. The final structure should then look like this –

.
└── startup/
    ├── startup.sh
    └── nginx-conf/
        └── nginx.conf

Next we need to edit the startup.sh we just created and add the following –

#!/bin/bash

echo "Replacing default nginx configuration for custom config"

cp /home/startup/nginx-conf/nginx.conf /etc/nginx/sites-available/default
 
echo "Reloading nginx to apply new custom config"

service nginx reload

This is pretty simple, just copy the custom config we created from the persistent storage over to the app service containers local storage, overwriting the default NGINX config file. Then restart the NGINX service so the new config file takes effect.

Finally with all of the above in place all that remains is to set the app service startup command to the following –

/home/startup/startup.sh

After you save the setting it should prompt your app service to restart and as the containers within startup they will run the startup script copying across and applying the WordPress ready NGINX config.

There you have it, you should now be able to browse to your WordPress site and all should work as expected.

Now wondering how to set PHP.ini options such as max file upload size for your WordPress instance? This next post should help and builds on the startup script created above 🙂