Nginx Config Generator for WordPress

Nginx does not read .htaccess files. Everything you would normally put in one goes into a server block instead, and WordPress will not work at all without one particular line in it.

Fill in your domain and folder, tick what you want, and copy the block.

Include
Your server block

Save as /etc/nginx/sites-available/yourdomain, link it into sites-enabled, then run nginx -t before reloading. That test catches a typo before it takes the site down.

Am I even on Nginx?

If you are not sure, you probably are not. Most shared hosting is Apache or LiteSpeed, where .htaccess works and this page does not apply to you.

You are likely on Nginx if you run your own server on DigitalOcean, Hetzner, Vultr, Linode or AWS, or if you use a managed host built on it such as Kinsta, Cloudways or WP Engine. On managed hosting you usually cannot edit the config yourself, so the useful part of this page is understanding what is in it rather than pasting it.

The quick test: add a rule to .htaccess and see whether anything happens. If nothing does, you are on Nginx.

The two lines that matter most

Everything else in the generated block is a convenience. These two are not.

The permalink line

location / {
    try_files $uri $uri/ /index.php?$args;
}

Leave this out and your homepage works while every other page returns 404. Nginx goes looking for a real folder called /my-post/, does not find one, and gives up.

What the line says is: look for a real file, then a real folder, and if neither exists hand it to WordPress and let it work out what was meant. It is the direct equivalent of the WordPress block in .htaccess.

This is far and away the most common Nginx and WordPress problem, and the symptom is distinctive: homepage fine, everything else missing.

The PHP guard

try_files $fastcgi_script_name =404;

This one is a security line, and a surprising number of configs copied off forums leave it out.

Without it, Nginx will pass a path to PHP without first checking the file exists. Combined with an upload folder, that has been a route to running attacker code on the server for years. The line simply says: if the script is not really there, return 404 and do not involve PHP at all.

It costs nothing. Keep it.

Installing the config

  1. Save the block as /etc/nginx/sites-available/yourdomain.com
  2. Link it into the enabled folder:
    sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
  3. Test it before reloading: sudo nginx -t
  4. Only if that says the test is successful: sudo systemctl reload nginx

Step 3 is the one to never skip. nginx -t checks the file and tells you the exact line number of any mistake. Reloading a broken config takes the site down, and unlike .htaccess there is no undo by putting a file back, because Nginx is already refusing to start.

Also remove the default site if it is still enabled, or it may answer instead of yours:

sudo rm /etc/nginx/sites-enabled/default

The certificate paths

The block assumes Let’s Encrypt through Certbot, which is what most people use and is free:

ssl_certificate     /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

If you have no certificate yet, install Certbot and run sudo certbot --nginx. It obtains one, edits your config for you and sets up renewal. Do that first, then compare what it wrote with what is here.

If your certificate came from somewhere else, change those two paths to point at your own files.

What the optional parts do

Blocking xmlrpc.php

An old WordPress feature that almost nothing needs now, and a favourite target for brute force attempts because it lets an attacker try many passwords in one request.

Safe to block on most sites. Two exceptions: the Jetpack plugin uses it, and so does the official WordPress mobile app. If either matters to you, untick it.

Blocking PHP in the uploads folder

Quietly one of the most valuable lines in the whole config.

When a site is compromised, the attacker usually needs to get a PHP file onto the server and then run it. The uploads folder is the easiest place to put one, because it accepts files by design. This line means a PHP file sitting there can never be executed, which turns a serious break-in into a harmless file.

Caching for images and CSS

Tells browsers to keep images, stylesheets and fonts for a year, so returning visitors reuse what they already have.

The catch is the same as on Apache: replace an image with a file of the same name and returning visitors keep seeing the old one. Upload replacements under a new name and it never comes up.

Multisite

Only tick this if you actually run a WordPress network with sites in subfolders. The rewrite rules it adds strip the site prefix off admin and PHP requests, and on a normal single site they do nothing useful.

When something is wrong

Homepage works, every other page is 404

The permalink line is missing or mistyped. See the top of this page. This accounts for most Nginx and WordPress questions ever asked.

502 Bad Gateway

Nginx is running but cannot reach PHP. Nearly always the socket path in fastcgi_pass points at a PHP version you do not have installed.

Check what is actually there:

ls /run/php/

Then set the version in the tool to match what you see, or edit the line by hand.

The PHP file downloads instead of running

The location ~ .php$ block is missing or never matched, so Nginx is treating PHP as a plain file to hand over. Check the block is inside your server block and that nginx -t passes.

413 Request Entity Too Large

An upload was bigger than client_max_body_size. Raise it in the tool and regenerate. Note that PHP has its own separate limits in php.ini, so both have to be big enough.

nginx -t fails and mentions a duplicate

Two enabled configs claim the same server_name, usually because the default site is still linked. Remove it as shown above.

Coming from Apache

There is no converter that gets this right, because the two do not map cleanly. Rough guide:

  • Redirect and RewriteRule become return 301 or rewrite. Our redirect generator gives you both forms side by side.
  • Require all denied becomes deny all;
  • Options -Indexes has no equivalent, because Nginx does not list folders unless you switch it on.
  • ExpiresByType becomes expires inside a location block.
  • AuthUserFile becomes auth_basic_user_file. The .htpasswd file itself works unchanged, though Nginx will not accept bcrypt, so use the APR1 line from our htpasswd generator.

The biggest habit to unlearn: Nginx has no per folder config files. Everything lives in one place and needs a reload to take effect.