Skip to content
DEV TOOL

nginx Config Generator — HTTP/3, WebSocket, Security

Compose server blocks visually, pick a TLS profile, switch on HTTP/3 and WebSocket — or paste an existing .htaccess and convert it to nginx directives automatically.

Runs locally in the browser — configs are emitted in memory, nothing is uploaded.

Input mode

Presets

Load a stack, then tweak field by field.

Server block type

TLS profile

Rate-limit profile

Emits zone AND location reference — no orphaned zones.

nginx configuration

# nginx config generated by kittokit nginx-config-generator
# Validate with `nginx -t` before reloading. Validate Content-Security-
# Policy at csp-evaluator.withgoogle.com and HSTS at hstspreload.org.

# WebSocket connection-upgrade map (must be at http{} scope)
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

# Rate-limit zones — profile "gentle" (must be at http{} scope)
limit_req_zone $binary_remote_addr zone=gentle:10m rate=30r/s;
limit_conn_zone $binary_remote_addr zone=gentle_conn:10m;

# ── Let's Encrypt ACME challenge (webroot mode) ──
server {
  listen 80;
  listen [::]:80;
  server_name example.com www.example.com;

  # Certbot webroot challenge
  location /.well-known/acme-challenge/ {
    root /var/www/certbot;
  }

  # Everything else → HTTPS
  location / {
    return 301 https://$host$request_uri;
  }
}

# DNS-01 challenge (wildcard certs) is also possible — see:
# https://eff-certbot.readthedocs.io/en/stable/using.html#dns-plugins

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  # ── HTTP/3 + QUIC (nginx 1.25+) ──
  listen 443 quic reuseport;
  listen [::]:443 quic reuseport;
  http3 on;
  ssl_early_data on;
  add_header Alt-Svc 'h3=":443"; ma=86400' always;
  server_name example.com www.example.com;

  # ── TLS — Mozilla Modern (TLS 1.3 only) ──
  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
  ssl_protocols TLSv1.3;
  ssl_prefer_server_ciphers off;
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:10m;
  ssl_session_tickets off;
  # OCSP stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
  resolver 1.1.1.1 1.0.0.1 valid=300s;
  resolver_timeout 5s;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log warn;

  # ── Modern security headers ──
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
  add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;
  add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), interest-cohort=(), browsing-topics=(), fullscreen=(self)" always;
  add_header Cross-Origin-Opener-Policy "same-origin" always;
  add_header Cross-Origin-Resource-Policy "same-origin" always;
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;
  add_header X-Content-Type-Options "nosniff" always;
  add_header X-Frame-Options "DENY" always;

  # ── Compression (gzip + brotli when ngx_brotli is built) ──
  gzip on;
  gzip_vary on;
  gzip_comp_level 6;
  gzip_min_length 256;
  gzip_proxied any;
  gzip_types
    text/plain text/css text/javascript text/xml
    application/javascript application/json application/xml
    application/xml+rss application/atom+xml
    image/svg+xml font/woff2 application/wasm;
  # If you built nginx with ngx_brotli, also:
  # brotli on;
  # brotli_comp_level 6;
  # brotli_types text/plain text/css application/javascript application/json image/svg+xml;

  root /var/www/example.com;
  index index.html;

  # SPA fallback — every unknown route serves index.html
  location / {
    limit_req  zone=gentle burst=50 nodelay;
    limit_conn gentle_conn 100;
    try_files $uri $uri/ /index.html;
  }

  # Long-cache hashed assets
  location ~* \.(?:js|css|woff2?|svg|webp|avif|png|jpg|jpeg)$ {
    expires 1y;
    add_header Cache-Control "public, immutable" always;
  }
}

After deploy validate with `sudo nginx -t` and activate with `sudo systemctl reload nginx`.

Validate Content-Security-Policy at the external evaluator before deploying.

How It Works

  1. 01

    Paste text or code

    Paste your content into the input field or type directly.

  2. 02

    Instant processing

    The tool processes your content immediately and shows the result.

  3. 03

    Copy result

    Copy the result to your clipboard with one click.

Privacy

All calculations run directly in your browser. No data is sent to any server.

Build an nginx configuration block by block instead of merging three Stack Overflow snippets. Pick a server-block type (static, SPA fallback, reverse proxy, PHP-FPM, load balancer, TCP/UDP stream), choose a TLS profile, enable HTTP/3 + QUIC, WebSocket upgrade, modern security headers and a rate-limit profile. Alternatively, paste an existing `.htaccess` and the generator translates redirects, rewrites, headers and ErrorDocuments into nginx directives automatically.

01 — How to Use

How do you use this tool?

  1. Pick an input mode: visual block builder or paste `.htaccess` for conversion.
  2. Load a preset (SPA, Node reverse proxy, Python Uvicorn, WordPress, Strict-HTTPS, load balancer), then tune field by field.
  3. Set server-block type, TLS profile (Modern/Intermediate/Old), HTTP/2 and HTTP/3 toggles.
  4. Enable WebSocket upgrade on reverse-proxy/load-balancer blocks; pick a rate-limit profile (Gentle, API, Strict).
  5. Copy the config or download as `nginx.conf`. Validate with `sudo nginx -t` and activate with `sudo systemctl reload nginx`.

What does the nginx config generator do?

The generator is an editor for nginx server blocks. You pick one of eight block types (static hosting, SPA fallback, reverse proxy, PHP-FPM, load balancer, redirect-only, TCP stream, UDP stream), tune fields like server_name, root and upstream, and the generator assembles every directive in the correct order. Optionally, you can paste an existing .htaccess — the generator translates redirects, rewrites, headers and ErrorDocuments into nginx directives automatically. Everything happens in the browser. No upload, no account, no cookie banner.

Six presets cover the most common stack combinations:

  • SPA — static hosting — Single-page app with an Astro/SvelteKit/Vite build, long-cache for hashed assets, fallback to index.html, HTTP/3 on.
  • Node.js — reverse proxy — Node/Bun server behind nginx with WebSocket upgrade wired correctly.
  • Python — Uvicorn/ASGI — FastAPI or Starlette via Uvicorn, rate-limit “API”.
  • WordPress — PHP-FPM — PHP-FPM via Unix socket, wp-config.php and .env denied.
  • Strict HTTPS — A-grade audit — tuned for Mozilla Observatory A, TLS 1.3, COOP/COEP/CORP.
  • Load balancer — least_conn — nginx in front of three backend nodes, health failover via max_fails.

Which server-block types are built in?

Eight block types, grouped by use case:

  • Static hostingtry_files $uri $uri/ =404;. Classic for Hugo, 11ty, plain HTML.
  • SPA fallbacktry_files $uri $uri/ /index.html; plus long-cache for hashed assets. Default for any client-side routing app.
  • Reverse proxyproxy_pass to an upstream URL, with every standard X-Forwarded-* header. Optional WebSocket upgrade.
  • PHP-FPMfastcgi_pass to a Unix socket, with try_files permalinks and deny rules for wp-config.php, .env, .git.
  • Load balancerproxy_pass http://backend_pool; plus the matching upstream block template (commented because upstream {} lives outside server {}).
  • Redirect-onlyreturn 301 https://www.example.com$request_uri;. For www-canonical and domain migrations.
  • TCP stream — for stream {} scope, e.g. Postgres/Redis proxying. The block notes the scope switch.
  • UDP stream — analogous for DNS or QUIC forwarding.

How does the HTTP/3 + QUIC support work?

HTTP/3 supersedes HTTP/2 and uses QUIC instead of TCP. QUIC is UDP-based, folds TLS into the connection setup and eliminates HTTP/2’s head-of-line blocking. nginx has shipped QUIC in its official build since version 1.25 (previously only in the nginx-quic branch). The generator emits four directives the moment you enable HTTP/3:

listen 443 quic reuseport;
listen [::]:443 quic reuseport;
http3 on;
ssl_early_data on;
add_header Alt-Svc 'h3=":443"; ma=86400' always;

The Alt-Svc header signals to browsers that HTTP/3 is available on port 443; ma=86400 caches that hint for 24 hours. Browsers that do not yet speak HTTP/3 ignore the header and stay on HTTP/2.

What makes WebSocket upgrade correct?

WebSocket reverse-proxying breaks in most naive nginx configs because the connection upgrade is missing. The correct pattern consists of four parts:

  1. map directive at http {} scope — translates the client’s Upgrade header into a matching Connection value:
    map $http_upgrade $connection_upgrade {
      default upgrade;
      ''      close;
    }
  2. proxy_set_header Upgrade $http_upgrade; — forwards the upgrade signal to the backend.
  3. proxy_set_header Connection $connection_upgrade; — overrides nginx’s default Connection: close on reverse-proxied requests.
  4. proxy_read_timeout 86400s; — sets the read timeout to 24 hours so idle WebSockets are not dropped after the 60-second default.

The generator wires all four automatically as soon as you tick the WebSocket checkbox on a reverse- proxy or load-balancer block. This is the most common pain point in WebSocket setups — and the main reason for “why does my connection keep dropping” threads in forums.

Which security headers are emitted?

Six headers belong in every modern nginx block (see Mozilla Web Security Guidelines):

  • Strict-Transport-Security — HSTS with max-age=31536000; includeSubDomains; preload. Preload is on by default; a toggle lets you disable it for subdomains that still need HTTP fallbacks.
  • Content-Security-Policy — Starter value with default-src 'self', frame-ancestors 'none'. Toggle for report-only mode during rollout.
  • Permissions-Policy — Everything off by default (camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=(), browsing-topics=()).
  • Cross-Origin-Opener-Policysame-origin against window.opener tampering.
  • Cross-Origin-Embedder-Policy — Optional require-corp for SharedArrayBuffer use cases.
  • Cross-Origin-Resource-Policysame-origin blocks cross-origin embeds of your own assets.

Every header carries the always flag. Without always nginx drops headers on 4xx/5xx responses — which many audit tools (Mozilla Observatory, securityheaders.com) count as an A-level violation. Before deploying, validate the CSP at the external CSP Evaluator.

How does the rate-limit profile work?

nginx rate limiting combines two directives:

  • limit_req_zone at http {} scope — reserves shared memory and defines the rate.
  • limit_req zone=<name> burst=<n> nodelay; inside the location block — references the zone.

Common bug in public generators: the zone is emitted, but the location reference is missing — the zone stays orphaned (see the open issue in the most popular online generator). The generator emits both together and injects the reference into the first location block of the server block. Three profiles:

  • Gentle — 30 r/s, burst 50, limit_conn 100. For normal websites with real user traffic.
  • API — 10 r/s, burst 20, limit_conn 50. For REST/GraphQL APIs with token-based throttling.
  • Strict — 5 r/s, burst 10, limit_conn 20. For login endpoints, webhook receivers, critical routes.

burst defines how many requests beyond the rate are accepted before 429 responses fire. nodelay means: burst requests are processed immediately, not buffered against the rate. Without nodelay, nginx would artificially delay a burst request, which is rarely the desired behaviour in practice.

What does the .htaccess to nginx converter do?

Apache and nginx have different configuration models: Apache reads .htaccess per request (costs performance, gives flexibility), nginx parses its configuration once at reload (faster, no per-request override). A full 1:1 conversion does not exist, but the important directives map:

Apachenginx
Redirect 301 /old /newrewrite ^/old$ /new permanent;
Redirect 302 /a /brewrite ^/a$ /b redirect;
RewriteRule ^foo$ /bar [L]rewrite ^foo$ /bar last;
RewriteRule … [R=301,L]rewrite … permanent;
Header always set X-Frame-Options DENYadd_header X-Frame-Options "DENY" always;
ErrorDocument 404 /404.htmlerror_page 404 /404.html;
Options -Indexesautoindex off;
Require ip 192.168.1.1allow 192.168.1.1;
Deny from 203.0.113.4deny 203.0.113.4;
<IfModule>flattened with comment (nginx has no module-conditional)

Non-mappable directives come back as # TODO (could not convert): … comments. The converter is deterministic — same input, same output, no AI rate limit, no cloud round-trip. The instructions banner at the top (“paste inside your nginx server { ... } block”) is mandatory reading: the emitted directives belong in the right scope.

Which pain points does this generator help me avoid?

Four classics that show up in nginx tutorials over and over — and that the generator avoids:

  1. WebSocket dropping after 60 seconds — the usual cause is missing proxy_read_timeout 86400s; plus the incorrect upgrade headers. The generator wires both automatically.
  2. Rate-limit zone without reference — the limit_req_zone gets emitted, but limit_req in the location block is missing. Effect: zero. The generator emits both together.
  3. Security headers disappearing on error pages — without always nginx drops headers on 4xx/5xx responses. The generator sets always on every add_header.
  4. HTTP/3 misconfigured — missing reuseport, missing ssl_early_data, no Alt-Svc header. The generator emits all four directives the moment HTTP/3 is on.

How is privacy handled?

Pure-client. The generator runs entirely in the browser. There is no server endpoint for config validation, no telemetry call, no cookie, no account. Close the tab and your settings are gone — the generator does not persist anything. If you want a history, download the nginx.conf after each edit and version it in your repo.

What is intentionally not built?

  • No live nginx -t validation — would require a server round-trip or a WebAssembly port of nginx itself. Instead, the generator header and the UI hint point to sudo nginx -t after deploy.
  • No automatic Let’s Encrypt issuance — the generator only emits the Certbot webroot snippet. Activating the certificate needs server access and is outside the browser scope.
  • No Nginx Proxy Manager GUI mode — if you want a GUI-first workflow, use Nginx Proxy Manager directly. This generator is for devs who keep configuration as code.
  • No ngx_mod_security rules — WAF tuning is a separate tool (too specialised, too ruleset- specific).
  • No Apache 2.2 variant.htaccess 2.2 is covered by the sibling tool htaccess-generator.
  • No caching-profile editor — the emitted gzip defaults and the long-cache rule in the SPA block are enough for the 90 % use case.

Where can I dig deeper?

Last updated:

You might also like