Nginx on Localhost

Nginx (pronounced "engine-x") is a high-performance web server and reverse proxy. In local development, it's most useful as a reverse proxy that sits in front of other services — letting you access a Node.js app at localhost:3000 via a clean domain like myapp.local, or routing multiple projects to different ports from a single entry point. Nginx also serves static files extremely efficiently and handles PHP applications via PHP-FPM.

Nginx default: localhost:80

Install Nginx

# Ubuntu / Debian
sudo apt update && sudo apt install nginx
sudo systemctl start nginx

# macOS (Homebrew)
brew install nginx
brew services start nginx   # Starts on port 8080 by default on Mac

# Windows — download from nginx.org and run nginx.exe
# Or use Laragon which bundles Nginx

# Docker
docker run -d -p 80:80 --name nginx nginx:alpine

Config File Structure

# /etc/nginx/nginx.conf (simplified)
http {
    include /etc/nginx/sites-enabled/*;   # Ubuntu
    # or: include /etc/nginx/conf.d/*.conf;  # CentOS/Docker
}

# /etc/nginx/sites-available/myapp.conf
server {
    listen 80;
    server_name myapp.local;
    root /var/www/myapp/public;
    index index.html index.php;
}

On Ubuntu, enable a config with: sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/ then sudo nginx -s reload.

PHP-FPM Setup

Nginx doesn't run PHP natively — it passes PHP requests to a PHP-FPM process. A typical PHP site configuration:

server {
    listen 80;
    server_name myapp.local;
    root /var/www/myapp/public;
    index index.php index.html;

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

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Reverse Proxy — Node.js, Python, etc.

The most common Nginx use case in local development: proxy a running backend app so you can reach it at a clean URL instead of typing a port number:

# Proxy localhost:3000 (Node.js app) to myapp.local
server {
    listen 80;
    server_name myapp.local;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Add 127.0.0.1 myapp.local to your /etc/hosts (or C:\Windows\System32\drivers\etc\hosts) to make myapp.local resolve locally.

Multiple Projects — Different Domains

# /etc/nginx/sites-available/api.local.conf
server {
    listen 80;
    server_name api.local;
    location / { proxy_pass http://localhost:8000; }
}

# /etc/nginx/sites-available/frontend.local.conf
server {
    listen 80;
    server_name frontend.local;
    location / { proxy_pass http://localhost:5173; }
}

HTTPS Locally with mkcert

For testing HTTPS locally (required for some browser APIs and service workers), use mkcert to generate trusted local certificates:

# Install mkcert (macOS)
brew install mkcert
mkcert -install  # Adds CA to system trust store

# Generate cert for your local domain
mkcert myapp.local

# Nginx HTTPS config
server {
    listen 443 ssl;
    server_name myapp.local;
    ssl_certificate     /path/to/myapp.local.pem;
    ssl_certificate_key /path/to/myapp.local-key.pem;
    location / { proxy_pass http://localhost:3000; }
}
server {
    listen 80;
    server_name myapp.local;
    return 301 https://$host$request_uri;
}

Nginx vs Apache — Quick Comparison

FeatureNginxApache
ArchitectureEvent-driven, async (efficient under load)Process/thread-based (simpler)
PHPVia PHP-FPM (extra config step)mod_php (built-in, easier)
.htaccessNot supported — all config in server blocksSupported per-directory
Static filesFasterGood
Reverse proxyClean, widely usedPossible with mod_proxy
Windows supportLimitedGood (XAMPP, WAMP)

Troubleshooting

ProblemFix
Port 80 already in useApache, another Nginx, or a Docker container is on port 80 — stop it or change Nginx to port 8080
Config test before reloadsudo nginx -t — shows syntax errors before applying
502 Bad GatewayThe upstream app (Node, PHP-FPM) isn't running or isn't on the expected port
403 Forbidden on static filesFile permission issue — Nginx worker process needs read access to the web root
Reload vs restartnginx -s reload applies config without dropping connections. Use restart only if reload fails.