Nginx and Apache config generators compared: 6 tools tested on real servers

Updated 11 November 2026 · By Brent Alexis

Short answer: I gave six free config generators the same job, a reverse proxy to an app at 127.0.0.1:3000/api with HTTPS and WebSockets, and ran each one's output on real Nginx and Apache. Every config passed the syntax test on at least one server, but only two kept Let's Encrypt renewals working, and four of the other five Nginx tools wrote a proxy_pass line that sends /foo to the app as /apifoo. nginxconfig.io has by far the most options. Whichever you use, test the output before you trust it. ProxyForge is my own tool and is one of the six, so read its row with that in mind.

Tested on 11 November 2026 with each tool's output exactly as generated, on the official nginx image (Nginx 1.30.6), Debian 12's Nginx 1.22.1 and Debian 12's Apache 2.4.68. The app was an echo server that reports the exact request it receives. How it's tested

How the test worked

The tools are the ones that showed up in web search results for "nginx config generator" and "apache config generator" when I tested, plus DigitalOcean's nginxconfig.io, the best-known one. Each got the same settings: domain example.com, backend 127.0.0.1:3000/api, HTTPS with Let's Encrypt certificate paths, a redirect from HTTP to HTTPS, and WebSockets and gzip wherever the tool offered them. Anything else stayed at the tool's default. I saved each output without editing it and loaded it onto fresh servers, then sent every config the same requests.

The backend address with a path (/api) is deliberate: it's a common setup and the one that trips generators up. If your backend has no path, the "/foo reached the app as" columns below don't apply to you.

Nginx results

Toolnginx -t on 1.30 / 1.22/foo reached the app asRenewal checkWebSockets
nginxconfig.ioPasses, with warnings/apifooWorksWorks
NetOzPasses (warning on 1.30)/foo (no path option)FailsNot offered
Orbit2xPasses, with warningsEvery request got 400 (its proxy_pass sends /apifoo)FailsFails (400)
BrowserUtilsPasses on 1.30, fails on Debian 12/apifooFailsWorks
DevToolLabPasses (warning on 1.30)/apifooFails (403)Works
ProxyForge (mine)Passes on 1.30; on 1.22 only with Older Nginx ticked/api/fooWorksWorks

Renewal check means Let's Encrypt's check for http://example.com/.well-known/acme-challenge/..., followed through any redirect the way Let's Encrypt follows it. Where it fails, the request reaches your app or is blocked instead of reaching the folder Certbot writes to, so the first certbot renew fails weeks later. That's exactly the problem in Let's Encrypt renewal fails behind an HTTPS redirect.

The warnings, word for word from nginx -t (file names and line numbers left off):

[warn] the "listen ... http2" directive is deprecated, use the "http2" directive instead
[warn] "ssl_stapling" ignored, no OCSP responder URL in the certificate
[warn] could not build optimal proxy_headers_hash, you should increase either proxy_headers_hash_max_size: 512 or proxy_headers_hash_bucket_size: 64; ignoring proxy_headers_hash_bucket_size

The first one appeared on Nginx 1.30 for every tool except ProxyForge (see the http2 guide for why). The second came from nginxconfig.io, which turns on OCSP stapling; Let's Encrypt removed OCSP from its certificates in 2025, so those lines do nothing. The third came from Orbit2x's repeated header lines. Warnings don't stop Nginx, and HTTP/2 worked for every tool that started.

Apache results

Three of the tools generate Apache configs. All three passed apache2ctl configtest ("Syntax OK") on Debian 12. To keep the test fair, I enabled the same modules for all three: ssl, proxy, proxy_http, proxy_wstunnel, rewrite, headers and deflate.

Tool/foo reached the app asRenewal checkWebSocketsNotes
NetOz (virtual host)/foo (no path option)FailsNot offeredThe app got the header X-Real-IP: (null)
DevToolLab (virtual host)/api/fooFailsNot offeredNo X-Forwarded-Proto, so the app can't tell the visitor used HTTPS
ProxyForge (mine, .htaccess)/api/fooNot tested: HTTPS comes from its SSL GeneratorWorksThe app sees Host: 127.0.0.1:3000, a limit of .htaccess

Each tool in a few lines

nginxconfig.io (DigitalOcean)
Nginx only. By far the most options of the six: PHP frameworks, caching, logging, security headers including a Content Security Policy, and a link that saves all your settings. Open source under the MIT licence. It was the only other tool whose config passed the renewal check. Against that, its GitHub repository's last commit is dated 4 October 2024, and its output still uses the older listen ... http2 form and OCSP stapling. It also wrote proxy_pass http://127.0.0.1:3000/api; with no trailing slash, which gave /apifoo. It splits the config into six files, which is tidy but more to copy.
NetOz
The only one that also writes Caddy and HAProxy configs, from an Australian hosting company. Its form has no field for a backend path and no WebSocket option. Its port 80 block redirects everything, renewal checks included. Its Apache output sets X-Real-IP from an environment variable that wasn't set, so the app received the text (null).
Orbit2x
Nginx only. Unlike the others, it builds the config on its own server: your settings are sent to orbit2x.com. On test day, choosing its "Reverse Proxy" preset showed the error "Preset not found: reverse_proxy", and so did the Node.js and WebSocket presets; the "Custom" preset worked. The config it gave repeats every proxy_set_header line, so the app received two Host headers. HTTP's standard (RFC 9112) requires a server to reject a request with more than one Host header, and the test app did, with 400 Bad Request for every request.
BrowserUtils
Nginx only, short and simple, runs in your browser and includes WebSocket headers by default. Its output starts with gzip on; outside the server block. Debian's and Ubuntu's own nginx.conf already has that line, so on Debian 12 nginx -t failed with "gzip" directive is duplicate. Delete that first line on Debian or Ubuntu.
DevToolLab
Separate Nginx and Apache generators, both running in your browser. The Apache one got the path right (ProxyPass / http://127.0.0.1:3000/api/). The Nginx one adds HSTS and other security headers and WebSocket support, and adds www.example.com on its own. It also adds a rule that denies every path starting with a dot, which blocks /.well-known/ too. That's where the 403 on the renewal check came from.
ProxyForge (mine)
Nginx and Apache. It got the path right and included the renewal exception and WebSockets on both Nginx versions, but its default output fails on Nginx older than 1.25.1 (such as Debian 12's 1.22) until you tick Older Nginx. Its proxy config adds no security headers, because those come from a separate Security Headers Generator. It has far fewer options than nginxconfig.io, no load balancing, and no Caddy or HAProxy. Its Apache output is .htaccess by default, which can't pass the original Host header; see ProxyPass not allowed in .htaccess.

Which one should you use?

These results are from 11 November 2026. Tools change. If you make one of them and something here is out of date, email admin@proxyforge.tech and I'll re-test it and update this page.

Five checks for any generated config

  1. Run sudo nginx -t (or sudo apache2ctl configtest) and read the warnings, not just the last line.
  2. Request a test path and look at what your app receives. See why /api/foo becomes /apifoo.
  3. Run sudo certbot renew --dry-run now, not in 60 days.
  4. If you use WebSockets, open the part of your app that needs them and check the browser's developer tools for a 101 response.
  5. Check your headers with curl -I https://example.com.

Try the Config Generator →

How to use ProxyForge · FAQ: common errors · Guides · What's new · About