For years, reloading nginx config has been the same small ritual: edit a file, nginx -t, send a signal, then go read the logs to find out whether it actually worked. nginx 1.31.5 (mainline, released 2 September 2026) changes the second half of that. It brings the Control API to open-source nginx, so a config reload is now an HTTP request that answers back in JSON instead of a signal whose result you have to hunt for in error.log.
Here’s what it is, how to use it, and what it does - and doesn’t - change about running a reverse proxy.
The old way: reload, then go read the logs
nginx -s reload is the signal form of a reload. It sends SIGHUP to the master process, which re-reads and re-evaluates the configuration and starts new workers.[6] It’s fine for a human at a shell. It’s a bad fit for automation.
The problem is what the command doesn’t tell you. Exit code 0 only means the signal was delivered - not that the config parsed, not that the workers started, not that the new listen directive could bind. If the config has a typo, a socket path the worker can’t open, or a directive that doesn’t apply, nginx logs the error and keeps serving the last good config. So the operator’s loop becomes: reload, then tail error.log and correlate what you see with what you just changed.
That loop is exactly what the Control API replaces.
What the Control API is
The Control API is a small REST interface that lives in the nginx master process and handles runtime control and inspection. It was originally a feature of NGINX Plus - it shipped first in the Plus R37.0 long-term-support release - and 1.31.5 opens it up in the core engine.[2][3]
It exposes three endpoints:[2]
/1/control/processes- worker process information: name, PID, and exit state./1/control/config- the in-memory nginx configuration.GETit to inspect;PATCHit to trigger a reload./1/nginx- version and build information.
That’s the whole shape of it. Instead of sending a signal and guessing at the outcome, you make an HTTP request and read a JSON response.
Version requirement: mainline 1.31.5+
The Control API landed on the mainline branch. Open-source nginx needs 1.31.5 or newer to expose it.[1][3] The stable branch is a separate track (1.30.x)[7] and lags behind. So if your distro packages nginx from the stable branch, you won’t see the feature until you move to mainline.
That’s the practical constraint to plan around: for most “just works” VPS installs, the distro’s nginx is the stable branch. Moving to mainline is its own decision - same testing as any branch change - and it’s the price of admission for the Control API. The release is available as source and as binary packages for the major Linux distributions, so you don’t have to compile.[3]
Enable it
The Control API is off by default.[2] You turn it on at startup with the -l option, pointing it at a Unix-domain socket or a TCP port:[2]
sudo nginx -l unix:/tmp/nginx.sock
Use the Unix socket, not a port. The API is an unauthenticated interface to nginx internals; both the admin guide and the release writeup tell you to bind it to a Unix socket on a path only privileged users can reach, and to never expose it to the public internet.[2][3] The socket’s file permissions are the entire access control, so it has to live in a directory normal users can’t write to.
Reload with a PATCH
With the API running, a config reload is one request against /1/control/config:
curl --unix-socket /tmp/nginx.sock -X PATCH http://localhost/1/control/config
What changes is not the reload itself - a PATCH reloads about the same as nginx -s reload does. What changes is the outcome: the reload status comes back as JSON in the same HTTP response.[3]
That is the whole difference:
# old
nginx -s reload
# exit 0. Did it work? Go read /var/log/nginx/error.log.
# new
curl --unix-socket /tmp/nginx.sock -X PATCH http://localhost/1/control/config
# exit 0 AND a JSON body that tells you whether the reload succeeded.
A deploy script that runs nginx -s reload can’t tell a good reload from a bad one without parsing a log. A script that PATCHes the Control API branches on the response.
Inspect what’s running
The reload is the headline, but the other two endpoints are what make the API useful day to day. They’re GETs, so they’re read-only and safe to call while traffic is flowing:
curl --unix-socket /tmp/nginx.sock http://localhost/1/control/processes
curl --unix-socket /tmp/nginx.sock http://localhost/1/nginx
/1/control/processes returns worker process information - name, PID, exit state - without you parsing ps. /1/nginx returns version and build info, which is handy when you’re on mainline and want to confirm exactly what you’re running before you file a bug or decide to upgrade.[2]
The config endpoint is the one to actually shape your workflow around. GET /1/control/config returns a memory dump of the loaded configuration; PATCH triggers the reload.[2] So the API isn’t only about changing config - it’s also about asking the running process what it’s actually doing, which is useful when the config on disk and the config in memory have drifted.
What this changes about your op workflow
The old loop had two pain points: reload errors showed up in a log file, and you had to correlate them with a specific deploy. The Control API collapses both into a request/response pair.
For a box you reload by hand, the win is small. The win is for anything that reloads nginx programmatically. Three places it slots in:
- Deploy pipelines. After
git pulland a config change, call the Control API and fail the step on a non-success response instead of relying on atailandgrepforemerg. - Infrastructure-as-code and config managers. Trigger and verify a reload in the same step, and capture the JSON result in the run log.
- Canary / test configs. Push a candidate config, reload, read the verdict, and roll back on failure - no human watching a log.
Two things to keep in mind before you rely on it:
- It does not replace
nginx -t. Validate a candidate config before you apply it. The Control API gives you a clean machine-readable signal for the reload itself; it doesn’t remove the need to test. - A failed reload still keeps the last good config. On a config error, nginx checks syntax, then rolls back and keeps serving the old configuration while logging the problem - the same internals as a signal reload.[6] So the Control API isn’t a green light to push something you haven’t tested; it’s a faster, clearer way to learn that it failed.
And because it’s unauthenticated, the socket is your boundary. Keep it root-owned, not reachable by other users, and never bound to a public port.
The same release also touched routing
The Control API is the headline, but 1.31.5 shipped three other things that matter if you route traffic at the proxy. Not the subject of this article, but worth knowing:
- Predicate locations - a
locationcan now take a variable, evaluated at request time; it matches when the variable is non-empty and not"0".[5] Solocation $is_ai_scraper { ... }routes on something other than the URI path. - Early body reading (
client_body_early_read) - nginx can buffer and parse the request body before it picks alocation, so routing can depend on the payload, not just the path.[5] - Native JSON parsing (
ngx_http_json_module) - populate variables from a JSON document, e.g.json_set $method $request_body "method".[4] It’s a separate module, not built by default; enable it with--with-http_json_module, andjson_max_depthdefaults to 32.[4]
These pair with the Control API: if you’re going to reload and route more flexibly, the config surface grows, and having a reload that answers back is the operating side of that.
Before you upgrade
If you already run nginx as a reverse proxy, the change is small: validate the candidate config, start nginx with the socket, and switch the reload step from nginx -s reload to a PATCH over the socket. The rest of the config doesn’t change.
One thing worth doing regardless: this release includes a memory-safety fix tied to proxy_buffering on over HTTP/2. With a body-filter error and a slow client, a proxy that’s buffering HTTP/2 responses could hand a client the contents of freed heap memory. If you proxy HTTP/2 with buffering on, that’s a reason to be on 1.31.5 independent of the Control API.[3]
If you’re still standing up your proxy, start with the full config in our reverse-proxy guide, then add the Control API as the reload mechanism once traffic is flowing.
The short version
The Control API is a REST surface in the nginx master process: three endpoints, enabled with -l, reloaded with a PATCH. Its value isn’t a faster reload - it’s a synchronous, machine-readable answer to “did the config take?” Instead of reloading and reading error.log, you make one request and read JSON. To use it you need mainline 1.31.5+. Keep the socket off the network, and keep nginx -t in your workflow.
Sources
- nginx news: 2026 - release of nginx-1.31.5 mainline (control API, predicate locations, ngx_http_json_module), 02 Sep 2026. https://nginx.org/2026.html - checked 2026-09-27
- NGINX documentation - Control NGINX Processes at Runtime (Control API endpoints,
-lenable, security implications). https://docs.nginx.com/nginx/admin-guide/basic-functionality/runtime-control - checked 2026-09-27 - NGINX Community Blog - NGINX 1.31.5: Control API, predicate locations, early body inspection, and more. https://blog.nginx.org/blog/nginx-1-31-5-control-api-predicate-locations-early-body-inspection-and-more - checked 2026-09-27
- nginx - Module ngx_http_json_module (
json_set,json_max_depthdefault 32,--with-http_json_module). https://nginx.org/en/docs/http/ngx_http_json_module.html - checked 2026-09-27 - nginx - Module ngx_http_core_module (
locationpredicate,client_body_early_read, matching order). https://nginx.org/en/docs/http/ngx_http_core_module.html#location - checked 2026-09-27 - nginx - Controlling nginx (signals; HUP reload semantics; rolls back to and keeps the old configuration on failure). https://nginx.org/en/docs/control.html - checked 2026-09-27
- nginx - download (stable branch 1.30.x, mainline 1.31.x). https://nginx.org/en/download.html - checked 2026-09-27