"Curiosity is the very basis of education and if you tell me that curiosity killed the cat, I say only the cat died nobly." - Arnold Edinborough

Dealing with errors in nginx can be a frustrating experience if nginx isn’t configured correctly. Sadly, the default value for error log is less than optimal and some of the tricks to getting information from nginx are not obvious. This post is intended to be a reference for the tools nginx provide and how to configure them; as well as a general guide on what’s important when facing issues in nginx.

I will probably be vilified for putting this as the first thing, however. Knowing the basics is the most important part to understanding the more difficult issues not just related to syntax. Don’t skip basic nginx syntax or how an nginx request flows. Once you understand these concepts the non-error issues become far more tangible to work with.

The Error Log

For issues where there’s an error involved, having nginx configured correctly is absolutely essential. The error_log directive should be configured with an error log level of warn, either at server or http level depending on whether you want per-vhost logs or server wide logs.

error_log /var/log/nginx/file.log warn;

Anything other than this will just waste your time, either by giving too much information or not enough. With data in our error log lets analyse one of the potential errors that might show up. For this example I will use the often encountered “Unable to open primary script” error.

2013/06/03 12:56:12 [error] 2641#0: *20499586 FastCGI sent in stderr: "Unable to open primary script: /home/user/public_html/wp-login.php (No such file or directory)" while reading response header from upstream, client: 10.0.0.1, server: www.example.com, request: "GET //wp-login.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "www.example.com"

If the error looks daunting then not to worry, most of it can be mentally discarded after just a brief glance. Essentially, all the important information here can be distilled down to this.

2013/06/03 FastCGI sent "Unable to open primary script: /home/user/public_html/wp-login.php" request: "GET //wp-login.php"

What’s important here is first the date to know if it’s a recent error. If you’re monitoring your error log and get notice on new entries this can even be discarded. 2nd is the source of the error, our FastCGI backend in this case. The 3rd bit is the actual error and the 4th final bit is the request that caused it. These are all the essentials we need to.

  1. Know where the error came from.
  2. Know where we can most likely reproduce it.
  3. Which words to Google for.

Getting Errors from the Backend

If your backend outputs error messages to stderr then nginx will put these in the nginx error log as well. In the case of PHP this can be done by configuring the php.ini with log_errors set to stderr or on and having no error_log defined. Finally, you also have to set catch_workers_output = yes in the php-fpm.conf file.

For other backends the procedure might be different but the concept the same, make sure errors are output to stderr and nginx will log them.

Getting Debug Output

Despite experience, sometimes understanding what nginx is doing can be quite difficult. Turning on debug mode is usually quite an adventure and often not needed at all with a few handy tricks. Often, all you need is to know the value of a variable to nudge you in the right direction, for those situations we have the return directive.

What this directive allows us to do is output arbitrary text in any server or location block — and it supports variables. This is useful if you want to know if your request is flowing into a location like you expect it to or if you need to know the value of some variable. For instance, to understand what URI you’re asking PHP to execute you could do:

return 200 $request_filename;

It is also possible to output multiple variables in the same return directive.

return 200 $document_root-$fastcgi_script_name;

Finally, you can specify a custom log_format for the access_log, which can be useful if you can’t output debug info to the browser. I find this more cumbersome and usually prefer to have a proper testing environment, but it can do in a pinch.

My Book

If you liked the style of this blog post then check out my nginx book – Instant Nginx Starter. It introduces you to the syntax and flow of nginx and minimizes your time spent debugging errors.

flattr this!

Remember register globals? Remember how you had to code as if it was off, because it might be? Remember how you had to consider the security implications of it being on, because it might be? The might be and might not be is something which has plagued a lot of early PHP features. Register globals is in no way alone in this, in the effort of making things versatile the PHP developers managed to introduce the worst of both worlds and the best of none. At least for code where you can’t guarantee 100% control over the environment your code would be running in.

We see this even today with things such as short open tags. Ever been told you shouldn’t use them? Yeah, that’s primarily because they could be turned off thus leaking your source code into the document. (To a lesser degree it’s also about XML incompatibility)

Today I want to cover a very known feature, which many people don’t often think of as being in the same group as register globals and short open tags. Namely path info. The idea of path info is brilliant enough, it is most often used as a way to have SEO “friendly” URIs in cases where one might not be able to rewrite the URL. In short, you can have a URI like so:

flattr this!

Lately I’ve been working with a friend on a daily-deal aggregator. The Groupon-like sites are popping up everywhere and the market for aggregators is still fairly unfilled. My project, Alladeals, target the Swedish daily deals market and as such it needs to support Swedish characters. In future it might have to support other languages as well so I decided that UTF8 was the way to go. Since most webpages are encoded in UTF-8 these days it has been fairly painless to actually work with UTF-8 in PHP, that is, until yesterday.

PHP does not natively support UTF-8. This is fairly important to keep in mind when dealing with UTF-8 encoded data in PHP. Usually I’m pretty good at remembering that, however yesterday I happened upon a bug which could easily have gone unnoticed for months if not for some good luck.

The bug manifested itself in the deal titles, the design is not well suited for really long titles so it was decided that it would be best to make sure that the titles did not exceed a length of 140 characters. To cut the the title the following code was used:

flattr this!

This is part two in my caching series. Part one covered the concept behind the full page caching as well as potential problems to keep in mind. This part will focus on implementing the concept in actual PHP code. By the end of this you’ll have a working implementation that can cache full pages and invalidate them intelligently when an update happens.

Requirements

I’ll provide a fully functional framework with the simple application I used to get my benchmark figures. You’ll need the following software to be able to run it.

  • Nginx. I’m not sure which exact version but I generally use and recommend the latest development version.
  • PHP 5.3.0. I recommend at least 5.3.3 so you’ll have PHP-FPM for your fastcgi process management.
  • MySQL
  • Memcached

The Framework

I’ll be referencing the code on github instead of pasting it in this post to keep the size down, so you will probably want to download it.

flattr this!

Recently a post of mine was linked on yCombinator and for some reason a lot of the comments talked about the efficiency of WordPress. While it’s technically not related to the subject of the linked post I just want to point out that the performance of WordPress is pretty horrible regardless of whether you use Apache or nginx.

My friend Karl Blessing and I recently talked about WordPress caching plugins. He uses WP SuperCache and I use W3 Total Cache and he subsequently decided to do some WordPress caching benchmarks on the different methods. He’s done an awesome job and generated some pretty graphs for you to look at.

What I took away from the whole thing is that W3 Total Cache and WP SuperCache can offer similar performance if you’re willing to do static file caching, however, W3 Total Cache can offer a cleaner solution with caching in Memcached if you’re willing to sacrifice a bit of performance. The benefit to this, and why I use this method, is that you don’t need complicated rules in your nginx (or Apache) configuration files.

flattr this!

“No input file specified” or “Primary script unknown” in the error log is one of the most frequently encountered issues in nginx+PHP. People on serverfault and in the #nginx IRC channel asks for help with this so often that this post is mostly to allow me to be lazy and not have to type up the same answer every time.

This is actually an error from PHP and due to display_errors being 0ff people will often just get a blank page with no output. In a typical setup PHP will then send the error to stderr or stdout and nginx will pick up on it and log it in the nginx error log file. Thus people spend a ton of time trying to figure out why nginx isn’t working.

The root cause of the error is that PHP cannot find the file nginx is telling it to look for, and there are a few common cases that causes this.

flattr this!