Optimizing Images for Google PageSpeed with PHP and NGINX

Posted Monday 16 July 2018 by Urs Riggenbach.

One of the most critical factors in getting a high Google PageSpeed ranking is optimized images. Penalty is given for any image that is not compressed. This blog post will show you a framework-independent way to optimize your images with PHP and NGINX.

SPIP, Drupal, WordPress and the like store and serve uncompressed files usually from a single folder. In SPIP it is "local", in Wordpress it would be "wp-uploads". Instead of directly optimizing the source images in these folders, I’ve developed a PHP script that copies the files to a separate folder mimicking the same folder structures and filenames, and then optimizing the files. The specific software tools used for optimization are jpegtran and optipng.

 

Optimizing the Images
Deploy the following script in the root directory of your PHP framework.

<?php
//Optimizes images for delivery over web
function copyfile($in, $out,$outfolder) {
	//check fi fiel is in destiation
	if (file_exists($out)) {
		$return = "already_processed";
	}
	else {
		exec('mkdir -p ' . $outfolder);
		exec('cp ' . $in . ' ' . $out);
		exec('chmod 777 ' . $out);
		echo "copied file: $in to $out";
		$return = "not_processed";
	}
	if (isset($_GET['all'])) {
	$return = "not_processed";
	}
	return $return;
}
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('/var/www/html/local'));
$files = array(); 
foreach ($rii as $file) {
	if ($file->isDir()){
		continue;
	}
	$files[] = $file->getPathname(); 
}
	foreach ($files as $value) {
	$file_input = $value;
	//regex to replace last folders
	$re = '/(\/var\/www\/html\/local)/';
	$subst = '/var/www/html/local_optimized';
	$file_output = preg_replace($re, $subst, $value);
	//regex to get folder name of file
	$file_output_pathinfo = pathinfo($file_output);
	$file_output_folder = $file_output_pathinfo['dirname'];
	echo $file_input . "\n";
	echo $file_output . "\n";
	echo $file_output_folder . "\n";
	if (exif_imagetype($value) == IMAGETYPE_PNG) {
		echo 'The picture is a PNG...
		';
		$processing_status = copyfile($file_input,$file_output,$file_output_folder);
		if($processing_status == "already_processed"){
			echo "already processed... nothing to do. \n \n";
		} else {
			echo "processing now... \n \n";
			$output = exec('optipng -o5 '.$file_output);
			echo $output;
		}
	}
	if (exif_imagetype($value) == IMAGETYPE_JPEG) {
		echo 'The picture is a JPG...
		';
		$processing_status = copyfile($file_input,$file_output,$file_output_folder);
		if($processing_status == "already_processed"){
			echo "already processed...\n\n";
		}in
		else{
			echo "processing now...\n\n";
			$output = exec("jpegoptim  --verbose --max=80 --strip-all --preserve --totals " . $file_output);
			echo $output;
		}
	}
}
?>

 

You can run the script directly from the command line, such as php filename.php or access the file over the internet via https://yourwebsite/filename.php (however, running over CLI and automating this with a cron-job is better as there is no PHP max execution time limit on the CLI, and optimizing images is a resource heavy process).

 

Configuring NGINX
Next we need to configure NGINX to serve images from the "local_optimized" folder as opposed to the "local" folder. Because the above script will run as Cron Job periodically, we want to fall back to the "local" folder when the iamge cannot be found in the "local_optimized" folder (yet).

In your NGINX website conf-file, make sure you add a "location" block for your main website ("/"), and then serve the "local_optimized" folder primarily and then the original "local" folder as backup:

#your main location block
      location / {
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header Host $host;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_pass https://10.0.0.10:443;
}


#your optimized image block
location /local/ {
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header Host $host;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass https://10.0.0.10:443/local_optimized/ ;
	proxy_intercept_errors on;
	recursive_error_pages on;
	error_page 404 = @static_image_https;
}
#your optimized image block fallback
location @static_image_https {
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header Host $host;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_pass  https://10.0.0.10:443;
}

Don’t forget to nginx -t and service nginx reload

 

Validate your Setup
As mentioned in the beginning, Google PageSpeed is a great tool to validate that your images are served compressed. To validate the script working, you can simply compare the filesizes of the images in the "local" folder versus the "local_optimized" folder.