Nginx, PHP5-FPM and Permission Denied Errors

After upgrading to Nginx 1.4.7 and PHP 5.4.28 (or 5.5.12), you may start seeing errors like the following:

2014/05/03 13:27:41 [crit] 4202#0: *1 connect() to unix:/var/run/php5-fpm.sock failed (13: Permission denied) while connecting to upstream, client: xx.xxx.xx.xx, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "xx.xx.xx.xx"

The fix is to edit /etc/php5/fpm/pool.d/www.conf and set the listen.mode to 666 (be sure to uncomment the line as well):

listen.mode = 0666

Load Balancing a Reverse Proxy with Nginx, PHP5-FPM & SSL

Continuing with our migration to Nginx (see previous posts here and here), the next set of servers to move were a load balanced group of PHP servers. Each server consists of an http server on port 80, an https server on port 443 and an upstream PHP server on port 8000.

  • 1 to n upstream PHP servers can be configured, either local or remote
  • configuration supports both http (80) and https (443)
  • SSL is terminated before being passed to the upstream server
  • PHP upstream server uses PHP5-FPM via a local socket
  • static files are served directly via Nginx and are set to max expires

There are also some future optimizations I’m currently looking at:

# round robin load balance pool
# servers can be local or remote
upstream lbpool {
ip_hash;
server 10.10.10.1:8000 max_fails=3 fail_timeout=120;
server 10.10.10.2:8000 max_fails=3 fail_timeout=120;
# additional servers
}
# http server
server {
listen 80;
server_name example.com;
access_log /var/log/nginx/example.com.80.access.log main;
error_log /var/log/nginx/example.com.80.error.log;
# proxy requests to the pool
# ensure ips are passed correctly
location / {
proxy_pass http://lbpool;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
port_in_redirect off;
}
}
# https server
# terminates SSL before passing the request to the proxy
server {
listen 443 ssl;
server_name example.com;
access_log /var/log/nginx/example.com.443.access.log main;
error_log /var/log/nginx/example.com.443.error.log;
ssl_certificate /etc/nginx/ssl/example.com.crt; # or .pem
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
# proxy requests to the pool
# ensure ips are passed correctly
location / {
proxy_pass http://lbpool;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
port_in_redirect off;
}
}
# upstream php server
server {
listen 8000;
server_name example.com;
root /home/webapps/example.com;
access_log /var/log/nginx/example.com.8000.access.log main;
error_log /var/log/nginx/example.com.8000.error.log;
location / {
index index.php;
# pass php requests to the front controller (http://wiki.nginx.org/Pitfalls#Front_Controller_Pattern_based_packages)
# but don't proxy everything (http://wiki.nginx.org/Pitfalls#Proxy_Everything)
try_files $uri $uri/ /index.php;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# deny access to .htaccess files,
# git & svn repositories, etc
location ~ /\.(?:ht|git|svn) {
deny all;
}
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# caching
location ~* \.(pdf|css|js|png|gif|jpg|jpeg|ico|swf|mov|doc|pdf|xls|ppt|docx|pptx|xlsx)$ {
expires max;
log_not_found off;
access_log off;
}
}

view raw
nginx.conf
hosted with ❤ by GitHub

Configuring Nginx with Jira & Sendy

As a follow up to my previous post, the next two applications that needed migration were our Jira and Sendy installations.

Here are the Nginx configs for both:

server {
listen 80;
server_name jira.example.com;
access_log /var/log/nginx/jira.example.com.access.log main;
error_log /var/log/nginx/jira.example.com.error.log;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
port_in_redirect off;
proxy_redirect http://jira.example.com:8080/jira /;
proxy_connect_timeout 300;
}
# deny access to .htaccess files
location ~ /\.ht {
deny all;
}
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

view raw
jira.nginx.conf
hosted with ❤ by GitHub

upstream php-sendy-example-com {
server unix:/var/run/php5-fpm.sock;
}
server {
listen 80;
server_name sendy.example.com;
root /home/webapps/sendy.example.com/;
access_log /var/log/nginx/sendy.example.com.access.log main;
error_log /var/log/nginx/sendy.example.com.error.log;
location / {
index index.html index.htm index.php;
try_files $uri $uri/ $uri.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-sendy-example-com;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location /l/ {
rewrite ^/l/([a-zA-Z0-9/]+)$ /l.php?i=$1 last;
}
location /t/ {
rewrite ^/t/([a-zA-Z0-9/]+)$ /t.php?i=$1 last;
}
location /w/ {
rewrite ^/w/([a-zA-Z0-9/]+)$ /w.php?i=$1 last;
}
location /unsubscribe/ {
rewrite ^/unsubscribe/(.*)$ /unsubscribe.php?i=$1 last;
}
location /subscribe/ {
rewrite ^/subscribe/(.*)$ /subscribe.php?i=$1 last;
}
# deny access to .htaccess files
location ~ /\.ht {
deny all;
}
}

view raw
sendy.nginx.conf
hosted with ❤ by GitHub

Configuring Nginx & CodeIgniter with Rewrite Rules

I’ve recently begun working with Nginx as we’re finally moving off of Apache in our infrastructure. The first thing I noticed is documentation for various types of configurations is a bit sporadic. A lot is outdated and many don’t use best practices.

The first application I’m working on is a CodeIgniter-based PHP site which needs a few rewrite rules to function properly. I dug upĀ this tutorial but it unfortunately uses a lot of if statements which I quickly learned are evil in Nginx.

Below is the configuration that I’ve settled upon (for now) which takes into account some best practices including:

  • uses php-fpm for the upstream server via a unix socket
  • redirects all http://www.example.com traffic to example.com
  • passes all requests under the root to the front-controller as long as they don’t exist (allows nginx to directly serve static files)
  • prevents uncontrolled requests from being passed to php
  • prevents any access to leftover .htaccess files

I’m hoping to add some of the additional rewrite functionality found in the tutorial at a later date.

upstream php {
server unix:/var/run/php5-fpm.sock;
}
server {
# enforce NO www
server_name http://www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
root /home/webapps/www.example.com/;
access_log /var/log/nginx/www.example.com.access.log main;
location / {
index index.php;
# pass requests to the front controller (http://wiki.nginx.org/Pitfalls#Front_Controller_Pattern_based_packages)
# but don't proxy everything (http://wiki.nginx.org/Pitfalls#Proxy_Everything)
try_files $uri $uri/ /index.php;
}
location ~ \.php$ {
# dont pass uncontrolled requests to php (http://wiki.nginx.org/Pitfalls#Passing_Uncontrolled_Requests_to_PHP)
try_files $uri =404;
fastcgi_pass php;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
# deny access to .htaccess files
location ~ /\.ht {
deny all;
}
}

view raw
example.com.conf
hosted with ❤ by GitHub