Knowledge Mark G
Hire Me

Free developer tool

Nginx Config Generator

Build an nginx server block for a reverse proxy, static site or PHP app - HTTPS, redirects, gzip and caching included.

  • No sign-up
  • Instant results
  • Runs in your browser

Site

Where your Node, Python or .NET app listens.

HTTPS and headers

nginx config

example.com.conf
# WebSocket upgrade header. A map must sit outside any server block - this file is
# included inside the http {} context, so here is fine.
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# Plain HTTP: send everything to the HTTPS canonical address.
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

# HTTPS on www.example.com: redirect to example.com. The certificate must cover both names.
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name www.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

    client_max_body_size 10m;

    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 5;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml application/json application/javascript application/xml application/rss+xml image/svg+xml;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 60s;
    }
}

Save as /etc/nginx/sites-available/example.com.conf, link it into sites-enabled, then run sudo nginx -t before sudo systemctl reload nginx.

No certificate yet? Get one first with sudo certbot certonly --nginx -d example.com -d www.example.com.

One config for the four common setups

Reverse proxy sends requests to an app on a local port - Node.js, Next.js, Python, .NET - and passes on the real client IP and protocol in X-Forwarded headers. Static site serves files from a folder. SPA does the same but sends unknown paths to index.html so React, Vue or Angular routing works on refresh. PHP-FPM hands .php files to PHP and routes everything else through index.php, the way Laravel and WordPress expect.

HTTPS and redirects done in one place

With HTTPS on, port 80 answers only with a 301 to the HTTPS address, and the non-canonical name (www or bare domain - you choose) redirects to the canonical one, so every page has a single address. That matters for SEO as much as for security. Certificate paths follow Let’s Encrypt’s layout; run certbot first if you do not have a certificate yet.

Two nginx traps this avoids

First: an add_header inside a location block silently drops every add_header inherited from the server block - including your security headers. The static-asset cache rule therefore uses expires, which does not have that effect. Second: from nginx 1.25.1, HTTP/2 is switched on with http2 on; and the old listen 443 ssl http2 form logs a deprecation warning. Untick the version switch for older servers.

After deploying, run the security headers checker and the website speed test against the site to confirm the headers, HTTP/2 and compression are really live.

Questions people ask

Where do I put the nginx config file?+

On Debian and Ubuntu, in /etc/nginx/sites-available/, linked into sites-enabled. On other systems, in /etc/nginx/conf.d/ with a .conf extension. Always run sudo nginx -t before reloading.

How do I set up nginx as a reverse proxy?+

Choose Reverse proxy, enter the address your app listens on (for example http://127.0.0.1:3000) and your domain. The config forwards requests and sets the Host and X-Forwarded headers your app needs.

Why do I need the WebSocket option?+

WebSockets start as an HTTP request that asks to upgrade. nginx drops the Upgrade header unless told to pass it on, so Socket.IO, live reload and real-time apps fail without it.

How do I redirect www to non-www?+

Pick 'Redirect www → domain' and the config adds a server block that answers www requests with a 301 to the bare domain, keeping the path.

My security headers disappear on some pages - why?+

An add_header inside a location block replaces all add_header lines from the server block. Repeat the headers in that location, or avoid add_header there, as this generator does.

Is it safe to turn on HSTS?+

Only once HTTPS works on every subdomain, because browsers will then refuse plain HTTP for a year. Start with a short max-age if you are unsure.