Archive for the ‘Wordpress’ category

Path_Info & PHP_SELF woes [NginX]

December 12th, 2009

3/31/2011 This has been updated to reflect a better configuration to be used with Nginx 0.8/0.9.

Over the last couple of years I’ve been constantly researching for a way to get the PHP environment variables to show up correctly. My latest pains were with PATH_INFO and PHP_SELF, which are now finally solved.

My current configuration are PHP-FPM (5.2.10) and NginX (0.8.29) on a CentOS 5.4 x64 VPS. (As of 2011, I’m now using PHP 5.3.6, Nginx 0.9.6 and FreeBSD 8.2)

Traditionally you would use a PHP configuration such as this:

	server {
		server_name  your-domain.com www.your-domain.com;
 
		location / {
			root html/default;
		}
 
		location ~ \.php$ {
			include fastcgi_params;
			fastcgi_param  SCRIPT_FILENAME  /usr/local/nginx/html/default$fastcgi_script_name;
			fastcgi_pass  127.0.0.1:9000;
		}
	}

Within fastcgi_params would be something like this:

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;
 
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
 
fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
 
fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;
 
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

While the above method may seem to work at first, you’ll quickly notice problems when it comes to using $_SERVER[‘PATH_INFO’] and $_PATH[‘PATH_TRANSLATED’], and often enough $_SERVER[‘PHP_SELF’] ends up being set incorrectly when you try to adjust for the two environment variables.

Here is a setup I’ve come to prefer, especially when it comes to having multiple virtual hosts that use PHP. Notable tips are commented below the line.

Simple Nginx configuration file with a single virtual host

worker_processes  1;
pid        logs/nginx.pid;
events { worker_connections  1024; }
http {
	include       mime.types;
	default_type  application/octet-stream;
	sendfile        on;
	keepalive_timeout  65;
 
	index index.html index.htm index.php;
	# Identical to Apache's DirectoryIndex, setting it in
	# the http block set it as a default for all server blocks within
 
	server {
		listen	80; 
		# since port 80 is set by default, you do not actually need
		# to set this, unless of course you are binding to a specific
		# address such as listen server-ip-address:80 or alternate port; 
 
		server_name  example.com www.example.com;
 
		root   html/example.com/;
		# You will want to set your root here, since otherwise
		# $document_root within the php block will not work
		# if you set it in the location block you would also have 
		# to set the php block within that location as well
 
		location / {
			# This would replace the typical mod_rewrite rules for wordpress
			# it can also be try_files $uri $uri/ @rewrites; where it goes to a 
			# location @rewrites { ... } where you can place rewrite rules if a file
			# or folder is not found.
 
			try_files $uri $uri/ /index.php;
		}
 
		location = /favicon.ico { access_log off; log_not_found off; }
		# If you haven't created a favicon for your site, you can keep
		# your access and error logs clean by turning off the logs
		# when a browser requests the fav icon (its also a good way
		# to keep your logs from filling with useless information)
 
		location ~ /\. { access_log off; log_not_found off; deny all; }
		# You want to make sure that Nginx does not serve any .hidden files
 
		include php.conf;
		# I prefer to keep my php settings in one file, so I can simply
		# paste this single line for each of my virtual hosts
	}
}

Now the php.conf file (which I’ve created in the /conf folder with nginx.conf)

fastcgi_intercept_errors on;
# this will allow Nginx to intercept 4xx/5xx error codes
# Nginx will only intercept if there are error page rules defined
# -- This is better placed in the http {} block as a default
# -- so that in the case of wordpress, you can turn it off specifically
# -- in that virtual host's server block
 
location ~ \.php {
	fastcgi_split_path_info ^(.+\.php)(/.+)$;
	# A handy function that became available in 0.7.31 that breaks down 
	# The path information based on the provided regex expression
	# This is handy for requests such as file.php/some/paths/here/ 
 
	fastcgi_param  PATH_INFO          $fastcgi_path_info;
	fastcgi_param  PATH_TRANSLATED    $document_root$fastcgi_path_info;
 
        fastcgi_param  QUERY_STRING       $query_string;
        fastcgi_param  REQUEST_METHOD     $request_method;
        fastcgi_param  CONTENT_TYPE       $content_type;
        fastcgi_param  CONTENT_LENGTH     $content_length;
 
        fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
        fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
        fastcgi_param  REQUEST_URI        $request_uri;
        fastcgi_param  DOCUMENT_URI       $document_uri;
        fastcgi_param  DOCUMENT_ROOT      $document_root;
        fastcgi_param  SERVER_PROTOCOL    $server_protocol;
 
        fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
        fastcgi_param  SERVER_SOFTWARE    nginx;
 
        fastcgi_param  REMOTE_ADDR        $remote_addr;
        fastcgi_param  REMOTE_PORT        $remote_port;
        fastcgi_param  SERVER_ADDR        $server_addr;
        fastcgi_param  SERVER_PORT        $server_port;
        fastcgi_param  SERVER_NAME        $server_name;
 
	fastcgi_pass   127.0.0.1:9000;
	fastcgi_index  index.php;
}

There you have it, PHP should now have correct environment variables. For example http://www.example.com/php.php/a/path/string/?var=foo would render the following results:

$_SERVER["QUERY_STRING"] -> var=foo
$_SERVER["SCRIPT_NAME"] -> /php.php
$_SERVER["SCRIPT_FILENAME"] -> /usr/local/nginx/html/default/php.php
$_SERVER["REQUEST_URI"] -> /php.php/a/path/string/?var=foo
$_SERVER["DOCUMENT_URI"] -> /php.php/a/path/string/
$_SERVER["DOCUMENT_ROOT"] -> /usr/local/nginx/html/default
$_SERVER["PATH_INFO"] -> /a/path/string/
$_SERVER["PATH_TRANSLATED"] -> /usr/local/nginx/html/default/a/path/string
$_SERVER["PHP_SELF"] -> /php.php/a/path/string/

So there you have it. A simple php block that will correctly assign the path environment variables, without having to use multiple blocks and patterns. And quite easy to simply assign to a new virtual host by simply pasting the include php; line.

WordPress Automatic Update with SSH

November 22nd, 2009

If you’re like me, you don’t even want the insecure FTP protocol running on your server, but by default wordpress doesn’t even give you the option of using SSH to automatically upgrade your plugins, or wordpress itself.

I’m using a barebone CentOS server for this site, running on the Nginx webserver. I do not have a FTP server installed, and would very much prefer not to have one. Right now the only way to get into the server is via SSH. Below is a working configuration added to the wp-config.php file.

define('FS_METHOD', 'direct'); // 'ssh' is also an option, but did not work for my setup
define('FTP_BASE', '/opt/local/nginx/html/domain.com/');
define('FTP_CONTENT_DIR', '/opt/local/nginx/html/domain.com/wp-content/');
define('FTP_PLUGIN_DIR ', '/opt/local/nginx/html/domain.com/wp-content/plugins/');
define('FTP_PUBKEY', '/home/username/.ssh/id_rsa.pub');
define('FTP_PRIKEY', '/home/username/.ssh/id_rsa');
define('FTP_USER', 'username');
define('FTP_HOST', 'your-domain.com:22');

You can generate a public/private key by executing the following:

$ ssh-keygen

It will ask you where you wish to save the key (the default path usually /home/username/.ssh/id_rsa should be fine), followed by a passphrase which you can just leave blank for this purpose, then the location of the public key which is fine at its default location (usually /home/username/.ssh/id_rsa.pub)

You’ll also want to create an authorized key file by copying the public key into authorized_keys. We also need to make sure to set the proper permissions.

$ cd ~/.ssh
$ cp id_rsa.pub authorized_keys
$ cd ~/
$ chmod 755 .ssh
$ chmod 644 .ssh/*

By using the key pair shown in the configuration above you only have to supply the SSH username, otherwise if you don’t want to use key pairs, you can instead provide your SSH password with the following line:

define('FTP_PASS', 'password');

Make sure that the folders where updates (such as plugins) will need to be performed are writable by wordpress. From there when you click “upgrade automatically” it should just simply happen.