Nginx Review

How Nginx and I met


Nginx is a web server. I don't remember the first time I heard of it, but it's one of the two most common (the other being Apache) and it was the one I ended up using for this site when I first set it up. I've been using it here and at my job for a couple years.

Simple configuration

Nginx's configuration is done mostly from a single file, and a simple setup can be 30 lines or less. All the basics work out of the box: When serving static files, it handles 304 perfectly, setting the `last-modified` and `etag` headers by default so 304 works in every browser. It handles directory redirects (making `/dir` redirect to `/dir/` which returns `/dir/index.html`) with two concise directives that are enabled by default.

For a simple site without dynamic content, a base install of Nginx with less than five settings customized is literally all you need to get all the features you'd expect. And the best part is how clean the configuration file stays - generally, if your application doesn't necessitate you thinking about a feature, you don't have to look at it it in your `nginx.conf`. It's either on by default or there's just one line enabling it.

Never inadequate for complex setups

While being excellent at simple setups, it can also do everything I can think of that doesn't belong in an application server (and some things that arguably do), and most of them with just a single directive or two. Their page I linked above lists features at great length, so I won't list everything again here.

Nginx supports `include` directives that let you modularize and put different virtual servers in different files, which is great if you have a complex setup or want to have a testing subdomain you can mess with safely and turn on or off by commenting a single line (like I do). I store a redirect map in its own file that I include, which lets me return 301s for the old URL whenever I change an article's filename by just adding one line, which doesn't even have to clutter the "real" configuration. (Although category redirects do. I'm not sure if there's a way to handle those with a `map`. But they're still only one line each with the `rewrite` directive.)

A pet feature of mine is the tailored response to HTTP being sent to the HTTPS port. I was surprised to find it, but it's pretty nice for how obscure it is.

Extremely flexible logging

Nginx logs every request by default, and you can configure what information it logs about them to an incredible extent. I used to have my Nginx on this server logging in JSON so I didn't have to do manual string parsing (albeit this requires a bit of a hack).

Difficult or impossible to serve a filename that would get redirected if requested directly

The `index`, `error_page` and `try_files` directives reprocess the rest of the logic, and as far as I can find, there's no way to get the other behavior. (If there is a way, please let me know in the comments.) This is the *only* thing I've come across where it seems like Nginx can't do it despite it being a pretty simple task. I ran into it in 2020/1/16 when I stripped the frivolous `.html` from all my articles; I needed to have the old URLs 301 to the new ones. What I wanted to do was have the output dir mirror the source dir, so the filenames on disk still had the `.html`, but have Nginx redirect requests to `*.html` to the short version, even though the long version was the canonical filename. The problem I couldn't get around was that `try_files` and the other directives would restart request processing, so if a request came to `/software/nginx.html`, it would get redirected to `/software/nginx`, but then when `try_files` tried to serve `/software/nginx.html`, *that* would get rewritten to `/software/nginx`.

After several hours of frustration I gave up and had my template script strip the extensions. My opinion of Nginx went down a lot that day.

The reason I couldn't use `if ( $request_uri` ... `)` in place of `location` (`$request_uri` is always the *original* request URI, not the one Nginx is currently processing after internal redirects) is because `$request_uri` includes the query string. There's no `$request_path`... although `$uri` captures only the path segment, it has another difference from `$request_uri`, which is that it is, like `location` and unlike `$request_uri`, not fixed to the original. This fact made `$uri` useless for solving this problem.

Feature bloat

I love that Nginx does every facet of its job adequately, but its feature set is *massive*, and a lot of them either seem redundant or don't belong in a web server. Just look at this list of modules:


Looking at the list of features of the `if` directive in the `rewrite` module, a lot of those seem questionable to me. If the web server is reading the file system to determine whether a file is a symbolic link, isn't that a sign that we should just be using a CGI script or something? What's next, an Nginx directive to respond to a request for a directory with a random file inside the directory?

Wait, are you serious?

Some other things Nginx does that totally don't belong in a web server (albeit most of these modules aren't compiled in by default, they are part of the Nginx repository:

Transform images... no, this isn't a joke.

Return empty GIFs... for some reason...

Concatenate prefixes and suffixes to the response body. Okay, I actually would've jumped on this feature a long time ago when I was first looking to abstract out a template for my articles, but I was naive back then. Page templating is great... but with HTML, just adding prefixes and suffixes doesn't cut it. It's a nice thought, but this feature is way too inflexible to be useful enough to justify adding it. This is what CGI is for.

There are several different ways to do redirects - the `return` directive (with its status code and URL parameters), and the `rewrite` directive with its host of variants determined by a keyword at the end. These two directives accomplish a lot of the same things, yet neither one can fully replace the other; `return` can't accomplish "silent" redirects (which return a different URL as if the client requested it directly) and `rewrite` can't do 307 or 308 redirects, only 301 and 302. I'm not sure what the best way is off the top of my head, but I'm skeptical that there isn't a better way to design this.

Verdict: if you need a web server and haven't settled yet, absolutely check out Nginx! It's easy to use and almost certainly handles your use case.


subscribe via RSS

Proxied content from gemini://yujiri.xyz/software/nginx.gmi

Gemini request details:

Original URL
Status code
text/gemini; lang=en
Proxied by

Be advised that no attempt was made to verify the remote SSL certificate.

What is Gemini?